Skip to content

Commit

Permalink
Add --disable-default-rules flag (#132)
Browse files Browse the repository at this point in the history
* Add ability to disable default rules with `--disable-default-rules`

woke will error if you run it with `--disable-default-rules` without providing your own rules in a config file (because that would mean you're running woke without any rules, which is pointless)

* Fixup some tests

* Update docs

* Fix markdownlint error w/ whitespace

* Add debug when disabling default rules
  • Loading branch information
caitlinelfring authored Nov 7, 2021
1 parent 2058e2c commit e1176dc
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 49 deletions.
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

0 comments on commit e1176dc

Please sign in to comment.