Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --disable-default-rules flag #132

Merged
merged 6 commits into from
Nov 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ THE SOFTWARE.
package cmd

import (
"errors"
"fmt"
"os"
"runtime"
Expand All @@ -44,12 +45,13 @@ import (

var (
// flags
exitOneOnFailure bool
cfgFile string
debug bool
stdin bool
outputName string
noIgnore bool
exitOneOnFailure bool
cfgFile string
debug bool
stdin bool
outputName string
noIgnore bool
disableDefaultRules bool

// Version is populated by goreleaser during build
// Version...
Expand All @@ -73,6 +75,8 @@ Provide a list file globs for files you'd like to check.`,
RunE: rootRunE,
}

var ErrNoRulesEnabled = errors.New("no rules enabled: either configure rules in your config file or remove the `--disable-default-rules` flag")

func rootRunE(cmd *cobra.Command, args []string) error {
setDebugLogLevel()
runtime.GOMAXPROCS(runtime.NumCPU())
Expand All @@ -86,11 +90,15 @@ func rootRunE(cmd *cobra.Command, args []string) error {
Msg("woke completed")
}()

cfg, err := config.NewConfig(viper.ConfigFileUsed())
cfg, err := config.NewConfig(viper.ConfigFileUsed(), disableDefaultRules)
if err != nil {
return err
}

if len(cfg.Rules) == 0 {
return ErrNoRulesEnabled
}

var ignorer *ignore.Ignore
if !noIgnore {
ignorer = ignore.NewIgnore(cfg.IgnoreFiles)
Expand Down Expand Up @@ -136,6 +144,7 @@ func init() {
rootCmd.PersistentFlags().BoolVar(&debug, "debug", false, "Enable debug logging")
rootCmd.PersistentFlags().BoolVar(&noIgnore, "no-ignore", false, "Ignored files in .gitignore, .ignore, .wokeignore, .git/info/exclude, and inline ignores are processed")
rootCmd.PersistentFlags().StringVarP(&outputName, "output", "o", printer.OutFormatText, fmt.Sprintf("Output type [%s]", printer.OutFormatsString))
rootCmd.PersistentFlags().BoolVar(&disableDefaultRules, "disable-default-rules", false, "Disable the default ruleset")
}

// GetRootCmd returns the rootCmd, which should only be used by the docs generator in cmd/docs/main.go
Expand Down
84 changes: 65 additions & 19 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,43 @@ func BenchmarkRootRunE(b *testing.B) {
}
}

func TestInitConfig(t *testing.T) {
func setTestConfigFile(t *testing.T, filename string) {
origConfigFile := viper.ConfigFileUsed()
t.Cleanup(func() {
cfgFile = ""
debug = false
})
debug = true
t.Run("good config", func(t *testing.T) {
cfgFile = "../testdata/good.yml"
initConfig()
viper.SetConfigFile(origConfigFile)
})
viper.SetConfigFile(filename)
}

t.Run("no config", func(t *testing.T) {
cfgFile = ""
initConfig()
})
func TestInitConfig(t *testing.T) {
tests := []struct {
desc string
cfgFile string
}{
{
desc: "good config",
cfgFile: "../testdata/good.yml",
},
{
desc: "no config",
cfgFile: "",
},
{
desc: "invalid config",
cfgFile: "../testdata/invalid.yml",
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
t.Cleanup(func() {
cfgFile = ""
initConfig()
})
cfgFile = tt.cfgFile
initConfig()
assert.Equal(t, tt.cfgFile, viper.ConfigFileUsed())
})
}
}

func TestParseArgs(t *testing.T) {
Expand All @@ -62,13 +84,9 @@ func TestParseArgs(t *testing.T) {

func TestRunE(t *testing.T) {
origStdout := output.Stdout
origConfigFile := viper.ConfigFileUsed()
t.Cleanup(func() {
exitOneOnFailure = false
noIgnore = false
// Reset back to original
output.Stdout = origStdout
viper.SetConfigName(origConfigFile)
})

t.Run("no findings found", func(t *testing.T) {
Expand All @@ -86,8 +104,7 @@ func TestRunE(t *testing.T) {
t.Run("no findings found with custom message", func(t *testing.T) {
buf := new(bytes.Buffer)
output.Stdout = buf

viper.SetConfigFile("../testdata/.woke-custom-exit-success.yaml")
setTestConfigFile(t, "../testdata/.woke-custom-exit-success.yaml")
err := rootRunE(new(cobra.Command), []string{"../testdata/good.yml"})
assert.NoError(t, err)

Expand All @@ -98,9 +115,38 @@ func TestRunE(t *testing.T) {

t.Run("findings w error", func(t *testing.T) {
exitOneOnFailure = true

t.Cleanup(func() {
exitOneOnFailure = false
})
err := rootRunE(new(cobra.Command), []string{"../testdata"})
assert.Error(t, err)
assert.Regexp(t, regexp.MustCompile(`^files with findings: \d`), err.Error())
})

t.Run("no rules enabled", func(t *testing.T) {
disableDefaultRules = true
t.Cleanup(func() {
disableDefaultRules = false
})

err := rootRunE(new(cobra.Command), []string{"../testdata"})
assert.Error(t, err)
assert.ErrorIs(t, err, ErrNoRulesEnabled)
})

t.Run("invalid printer", func(t *testing.T) {
outputName = "foo"
t.Cleanup(func() {
outputName = "text"
})
err := rootRunE(new(cobra.Command), []string{"../testdata"})
assert.Error(t, err)
assert.Equal(t, "foo is not a valid printer type", err.Error())
})

t.Run("invalid config", func(t *testing.T) {
setTestConfigFile(t, "../testdata/invalid.yaml")
err := rootRunE(new(cobra.Command), []string{"../testdata"})
assert.Error(t, err)
})
}
12 changes: 12 additions & 0 deletions docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,18 @@ rules:
- name: whitelist
```

### Disable all Default Rules

There may be a case where you want full control over the rules you want to run with woke.

You can either disable each default rule via the instructions above.

Or you can run woke with `--disable-default-rules` to completely disable all default rules.

!!! note
`woke` will fail to run if you use `--disable-default-rules` without providing your own rules
because that would mean running `woke` without any rules, which is pointless.

## Excluding Categories of Rules

You can also specify any number of rule categories to be excluded, or filtered out, from within your `woke` configuration. If any rules in a configuration file have matching categories, they will be excluded and will not be run against the target files.
Expand Down
17 changes: 10 additions & 7 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type Config struct {
}

// NewConfig returns a new Config
func NewConfig(filename string) (*Config, error) {
func NewConfig(filename string, disableDefaultRules bool) (*Config, error) {
var c Config
if len(filename) > 0 {
var err error
Expand All @@ -50,7 +50,7 @@ func NewConfig(filename string) (*Config, error) {
log.Debug().Msg("no config file loaded, using only default rules")
}

c.ConfigureRules()
c.ConfigureRules(disableDefaultRules)
logRuleset("all enabled", c.Rules)

return &c, nil
Expand Down Expand Up @@ -78,13 +78,16 @@ func (c *Config) inExistingRules(r *rule.Rule) bool {
// Configure RegExps for all rules
// Configure IncludeNote for all rules
// Filter out any rules that fall under ExcludeCategories
func (c *Config) ConfigureRules() {
for _, r := range rule.DefaultRules {
if !c.inExistingRules(r) {
c.Rules = append(c.Rules, r)
func (c *Config) ConfigureRules(disableDefaultRules bool) {
if disableDefaultRules {
log.Debug().Msg("disabling default rules")
} else {
for _, r := range rule.DefaultRules {
if !c.inExistingRules(r) {
c.Rules = append(c.Rules, r)
}
}
}

logRuleset("default", rule.DefaultRules)
var excludeIndices []int

Expand Down
42 changes: 26 additions & 16 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func TestNewConfig(t *testing.T) {
defaultRules[i] = fmt.Sprintf("%q", rule.DefaultRules[i].Name)
}

c, err := NewConfig("testdata/good.yaml")
c, err := NewConfig("testdata/good.yaml", false)
assert.NoError(t, err)
enabledRules := make([]string, len(c.Rules))
for i := range c.Rules {
Expand All @@ -54,7 +54,7 @@ func TestNewConfig(t *testing.T) {
})

t.Run("config-good", func(t *testing.T) {
c, err := NewConfig("testdata/good.yaml")
c, err := NewConfig("testdata/good.yaml", false)
assert.NoError(t, err)

expectedRules := []*rule.Rule{}
Expand All @@ -81,7 +81,7 @@ func TestNewConfig(t *testing.T) {
Rules: expectedRules,
IgnoreFiles: []string{"README.md", "pkg/rule/default.go", "testdata/good.yaml"},
}
expected.ConfigureRules()
expected.ConfigureRules(false)

assert.EqualValues(t, expected.Rules, c.Rules)

Expand All @@ -91,7 +91,7 @@ func TestNewConfig(t *testing.T) {

t.Run("config-empty-missing", func(t *testing.T) {
// Test when no config file is provided
c, err := NewConfig("")
c, err := NewConfig("", false)
assert.NoError(t, err)

expectedEmpty := &Config{
Expand All @@ -103,14 +103,14 @@ func TestNewConfig(t *testing.T) {

t.Run("config-missing", func(t *testing.T) {
// Test when no config file is provided
c, err := NewConfig("testdata/missing.yaml")
c, err := NewConfig("testdata/missing.yaml", false)
assert.Error(t, err)
assert.Nil(t, c)
})

t.Run("config-empty-success-message", func(t *testing.T) {
// Test when no config file is provided
c, err := NewConfig("testdata/empty-success-message.yaml")
c, err := NewConfig("testdata/empty-success-message.yaml", false)
assert.NoError(t, err)

// check default config message
Expand All @@ -119,7 +119,7 @@ func TestNewConfig(t *testing.T) {

t.Run("config-custom-success-message", func(t *testing.T) {
// Test when no config file is provided
c, err := NewConfig("testdata/custom-success-message.yaml")
c, err := NewConfig("testdata/custom-success-message.yaml", false)
assert.NoError(t, err)

// check default config message
Expand All @@ -128,7 +128,7 @@ func TestNewConfig(t *testing.T) {

t.Run("config-add-note-messaage", func(t *testing.T) {
// Test when it is configured to add a note to the output message
c, err := NewConfig("testdata/add-note-message.yaml")
c, err := NewConfig("testdata/add-note-message.yaml", false)
assert.NoError(t, err)

// check global IncludeNote
Expand All @@ -141,9 +141,9 @@ func TestNewConfig(t *testing.T) {
assert.Equal(t, false, *c.Rules[0].Options.IncludeNote)
})

t.Run("config-dont-add-note-messaage", func(t *testing.T) {
t.Run("config-dont-add-note-message", func(t *testing.T) {
// Test when it is nott configured to add a note to the output message
c, err := NewConfig("testdata/dont-add-note-message.yaml")
c, err := NewConfig("testdata/dont-add-note-message.yaml", false)
assert.NoError(t, err)

// check global IncludeNote
Expand All @@ -156,9 +156,19 @@ func TestNewConfig(t *testing.T) {
assert.Equal(t, true, *c.Rules[0].Options.IncludeNote)
})

t.Run("disable-default-rules", func(t *testing.T) {
c, err := NewConfig("testdata/good.yaml", true)
assert.NoError(t, err)
assert.Len(t, c.Rules, 3)

c, err = NewConfig("testdata/good.yaml", false)
assert.NoError(t, err)
assert.Len(t, c.Rules, len(rule.DefaultRules)+2)
})

t.Run("config-exclude-a-category", func(t *testing.T) {
// Test when configured to exclude a single category
c, err := NewConfig("testdata/exclude-single-category.yaml")
c, err := NewConfig("testdata/exclude-single-category.yaml", false)
assert.NoError(t, err)

expectedRules := []*rule.Rule{}
Expand All @@ -180,15 +190,15 @@ func TestNewConfig(t *testing.T) {
Rules: expectedRules,
ExcludeCategories: []string{"cat2"},
}
expected.ConfigureRules()
expected.ConfigureRules(false)

assert.EqualValues(t, expected.Rules, c.Rules)
assert.Equal(t, "No findings found.", c.GetSuccessExitMessage())
})

t.Run("config-exclude-multiple-categories", func(t *testing.T) {
// Test when configured to exclude multiple categories
c, err := NewConfig("testdata/exclude-multiple-categories.yaml")
c, err := NewConfig("testdata/exclude-multiple-categories.yaml", false)
assert.NoError(t, err)

expectedRules := []*rule.Rule{}
Expand All @@ -203,19 +213,19 @@ func TestNewConfig(t *testing.T) {
Rules: expectedRules,
ExcludeCategories: []string{"cat1", "cat2"},
}
expected.ConfigureRules()
expected.ConfigureRules(false)

assert.EqualValues(t, expected.Rules, c.Rules)
assert.Equal(t, "No findings found.", c.GetSuccessExitMessage())
})

t.Run("load-config-with-bad-url", func(t *testing.T) {
_, err := NewConfig("https://raw.githubusercontent.com/get-woke/woke/main/example")
_, err := NewConfig("https://raw.githubusercontent.com/get-woke/woke/main/example", false)
assert.Error(t, err)
})

t.Run("load-config-with-url", func(t *testing.T) {
c, err := NewConfig("https://raw.githubusercontent.com/get-woke/woke/main/example.yaml")
c, err := NewConfig("https://raw.githubusercontent.com/get-woke/woke/main/example.yaml", false)
assert.NoError(t, err)
assert.NotNil(t, c)
})
Expand Down
Loading