diff --git a/data/nullify.yaml b/data/nullify.yaml index 7aa9060..4416a8f 100644 --- a/data/nullify.yaml +++ b/data/nullify.yaml @@ -1,3 +1,4 @@ severity_threshold: medium ignore_dirs: ["data"] +ignore_pattens: ["*d"] email_notifications: ["notifications@nullify.cloud", "noreply@nullify.cloud"] diff --git a/go.mod b/go.mod index 9393b75..db89cb3 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/nullify-platform/config-file-parser go 1.19 require ( + github.com/gobwas/glob v0.2.3 github.com/stretchr/testify v1.8.4 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 925e02e..7c10593 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= diff --git a/pkg/models/config.go b/pkg/models/config.go index ebcaaff..58f2345 100644 --- a/pkg/models/config.go +++ b/pkg/models/config.go @@ -3,6 +3,7 @@ package models type Configuration struct { SeverityThreshold string `yaml:"severity_threshold"` IgnoreDirs []string `yaml:"ignore_dirs"` + IgnorePatterns []string `yaml:"ignore_patterns"` EmailNotifications []string `yaml:"email_notifications"` SecretsWhitelist []string `yaml:"secrets_whitelist"` } diff --git a/pkg/parser/defaults.go b/pkg/parser/defaults.go index 2697ab8..44431de 100644 --- a/pkg/parser/defaults.go +++ b/pkg/parser/defaults.go @@ -8,5 +8,6 @@ func NewDefaultConfig() *models.Configuration { return &models.Configuration{ SeverityThreshold: DefaultSeverityThreshold, IgnoreDirs: []string{}, + IgnorePatterns: []string{}, } } diff --git a/pkg/parser/parse_test.go b/pkg/parser/parse_test.go index 9adb0b3..7e38413 100644 --- a/pkg/parser/parse_test.go +++ b/pkg/parser/parse_test.go @@ -26,6 +26,7 @@ func TestParseConfiguration(t *testing.T) { expected: &models.Configuration{ SeverityThreshold: models.SeverityMedium, IgnoreDirs: nil, + IgnorePatterns: nil, SecretsWhitelist: nil, }, }, @@ -35,6 +36,7 @@ func TestParseConfiguration(t *testing.T) { expected: &models.Configuration{ SeverityThreshold: models.SeverityHigh, IgnoreDirs: []string{"data"}, + IgnorePatterns: []string{"*d"}, SecretsWhitelist: []string{"secretPassword", "superSecretPassword"}, }, }, @@ -44,6 +46,7 @@ func TestParseConfiguration(t *testing.T) { expected: &models.Configuration{ SeverityThreshold: models.SeverityMedium, IgnoreDirs: nil, + IgnorePatterns: nil, SecretsWhitelist: nil, }, }, @@ -53,6 +56,7 @@ func TestParseConfiguration(t *testing.T) { expected: &models.Configuration{ SeverityThreshold: models.SeverityLow, IgnoreDirs: nil, + IgnorePatterns: nil, SecretsWhitelist: nil, }, }, @@ -62,6 +66,7 @@ func TestParseConfiguration(t *testing.T) { expected: &models.Configuration{ SeverityThreshold: models.SeverityMedium, IgnoreDirs: nil, + IgnorePatterns: nil, SecretsWhitelist: []string{"password"}, }, }, @@ -72,6 +77,27 @@ func TestParseConfiguration(t *testing.T) { SeverityThreshold: models.SeverityMedium, IgnoreDirs: nil, SecretsWhitelist: nil, + IgnorePatterns: nil, + }, + }, + { + name: "user provided empty ignore patterns", + data: `ignore_patterns: `, + expected: &models.Configuration{ + SeverityThreshold: models.SeverityMedium, + IgnoreDirs: nil, + IgnorePatterns: nil, + SecretsWhitelist: nil, + }, + }, + { + name: "user provided glob in ignore patterns", + data: `ignore_patterns: ["*d"]`, + expected: &models.Configuration{ + SeverityThreshold: models.SeverityMedium, + IgnoreDirs: nil, + IgnorePatterns: []string{"*d"}, + SecretsWhitelist: nil, }, }, } { diff --git a/pkg/validator/glob.go b/pkg/validator/glob.go new file mode 100644 index 0000000..9e7c904 --- /dev/null +++ b/pkg/validator/glob.go @@ -0,0 +1,21 @@ +package validator + +import ( + "github.com/gobwas/glob" + "github.com/nullify-platform/config-file-parser/pkg/models" +) + +func ValidateGlob(config *models.Configuration) bool { + if config.IgnorePatterns == nil { + return true + } + + for _, pattern := range config.IgnorePatterns { + _, err := glob.Compile(pattern) + if err != nil { + return false + } + } + + return true +} diff --git a/pkg/validator/glob_test.go b/pkg/validator/glob_test.go new file mode 100644 index 0000000..961c694 --- /dev/null +++ b/pkg/validator/glob_test.go @@ -0,0 +1,115 @@ +package validator + +import ( + "fmt" + "testing" + + "github.com/nullify-platform/config-file-parser/pkg/models" + "github.com/nullify-platform/config-file-parser/pkg/parser" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestValidGlobs(t *testing.T) { + for _, scenario := range []struct { + name string + config *models.Configuration + expected bool + }{ + { + config: &models.Configuration{}, + expected: true, + }, + { + config: &models.Configuration{IgnorePatterns: []string{}}, + expected: true, + }, + { + config: &models.Configuration{IgnorePatterns: []string{"*[abc]"}}, + expected: true, + }, + { + config: &models.Configuration{IgnorePatterns: []string{"*[abc]", "*d"}}, + expected: true, + }, + { + config: &models.Configuration{IgnorePatterns: []string{"*[abc"}}, + expected: false, + }, + { + config: &models.Configuration{IgnorePatterns: []string{"*d", "*[abc"}}, + expected: false, + }, + { + config: &models.Configuration{IgnorePatterns: []string{"*[abc", "*d"}}, + expected: false, + }, + } { + t.Run(scenario.name, func(t *testing.T) { + isValid := ValidateGlob(scenario.config) + assert.Equalf(t, isValid, scenario.expected, fmt.Sprintf("failed test, globs: %s, len: %d\n", scenario.config.IgnorePatterns, len(scenario.config.IgnorePatterns))) + }) + } +} + +const validGlob string = ` +severity_threshold: medium +ignore_dirs: ["data"] +ignore_patterns: ["*[abc]"] +email_notifications: ["hello@gmail.com"] +` +const emptyGlob string = ` +severity_threshold: medium +ignore_dirs: ["data"] +ignore_patterns: ["*[abc]", "*d"] +email_notifications: ["hello@gmail.com"] +` + +const twoValidGlob string = ` +severity_threshold: medium +ignore_dirs: ["data"] +ignore_patterns: +email_notifications: ["hello@gmail.com"] +` + +const invalidGlob string = ` +severity_threshold: medium +ignore_dirs: ["data"] +ignore_patterns: ["*[abc"] +email_notifications: ["hello@gmail.com"] +` + +const endInvalidGlob string = ` +severity_threshold: medium +ignore_dirs: ["data"] +ignore_patterns: ["*d", "*[abc"] +email_notifications: ["hello@gmail.com"] +` + +const startInvalidGlob string = ` +severity_threshold: medium +ignore_dirs: ["data"] +ignore_patterns: ["*[abc", "*d"] +email_notifications: ["hello@gmail.com"] +` + +func TestParsingAndValidGlobs(t *testing.T) { + config1, err := parser.ParseConfiguration([]byte(validGlob)) + require.NoError(t, err) + require.Equal(t, true, ValidateGlob(config1)) + config2, err := parser.ParseConfiguration([]byte(emptyGlob)) + require.NoError(t, err) + require.Equal(t, true, ValidateGlob(config2)) + config3, err := parser.ParseConfiguration([]byte(twoValidGlob)) + require.NoError(t, err) + require.Equal(t, true, ValidateGlob(config3)) + config4, err := parser.ParseConfiguration([]byte(endInvalidGlob)) + require.NoError(t, err) + require.Equal(t, false, ValidateGlob(config4)) + config5, err := parser.ParseConfiguration([]byte(startInvalidGlob)) + require.NoError(t, err) + require.Equal(t, false, ValidateGlob(config5)) + config6, err := parser.ParseConfiguration([]byte(invalidGlob)) + require.NoError(t, err) + require.Equal(t, false, ValidateGlob(config6)) +} diff --git a/pkg/validator/validate.go b/pkg/validator/validate.go index 0a28a3b..22e15d6 100644 --- a/pkg/validator/validate.go +++ b/pkg/validator/validate.go @@ -6,5 +6,5 @@ import ( // ValidateConfig return true if provided configuration is valid func ValidateConfig(config *models.Configuration) bool { - return ValidateSeverityThreshold(config) && ValidateEmail(config) + return ValidateSeverityThreshold(config) && ValidateEmail(config) && ValidateGlob(config) }