Skip to content

Commit

Permalink
have validate function return ValidationReport instead of separate sl…
Browse files Browse the repository at this point in the history
…ices used to make a ValidationReport
  • Loading branch information
lhitchon committed Apr 1, 2018
1 parent 503d9b8 commit e02be90
Show file tree
Hide file tree
Showing 14 changed files with 62 additions and 56 deletions.
1 change: 0 additions & 1 deletion README.md
Expand Up @@ -243,4 +243,3 @@ Rules:
* Update value_from to handle JSON return values
* Need to include the search expression result that triggered a violation, include it in the Violation
* Create a Provider interface for AWS calls, create a mock for testing SecurityGroupLinter
* Create a new type for result of Validate - instead of (filesScanned, resourcesScanned, violations, error)
2 changes: 1 addition & 1 deletion assertion/types.go
Expand Up @@ -73,8 +73,8 @@ type (

// ValidationReport summarizes validation for resources using rules
ValidationReport struct {
Violations map[string]([]Violation)
FilesScanned []string
Violations []Violation
ResourcesScanned []ScannedResource
}

Expand Down
20 changes: 9 additions & 11 deletions cli/app.go
Expand Up @@ -64,8 +64,10 @@ func (i *arrayFlags) Set(value string) error {
}

func generateExitCode(report assertion.ValidationReport) int {
if len(report.Violations["FAILURE"]) > 0 {
return 1
for _, v := range report.Violations {
if v.Status == "FAILURE" {
return 1
}
}
return 0
}
Expand All @@ -81,9 +83,9 @@ func main() {
flag.Parse()

report := assertion.ValidationReport{
Violations: make(map[string]([]assertion.Violation), 0),
FilesScanned: make([]string, 0),
ResourcesScanned: make([]assertion.ScannedResource, 0),
Violations: []assertion.Violation{},
FilesScanned: []string{},
ResourcesScanned: []assertion.ScannedResource{},
}

for _, rulesFilename := range rulesFilenames {
Expand All @@ -102,15 +104,11 @@ func main() {
if *searchExpression != "" {
linter.Search(flag.Args(), ruleSet, *searchExpression)
} else {
filesScanned, resourcesScanned, violations, err := linter.Validate(flag.Args(), ruleSet, makeTagList(*tags), makeRulesList(*ids))
r, err := linter.Validate(flag.Args(), ruleSet, makeTagList(*tags), makeRulesList(*ids))
if err != nil {
fmt.Println("Validate failed:", err) // FIXME
}
for _, violation := range violations {
report.Violations[violation.Status] = append(report.Violations[violation.Status], violation)
}
report.FilesScanned = append(report.FilesScanned, filesScanned...)
report.ResourcesScanned = append(report.ResourcesScanned, resourcesScanned...)
report = combineValidationReports(report, r)
}
}
}
Expand Down
9 changes: 3 additions & 6 deletions cli/aws_resource_linter.go
Expand Up @@ -19,17 +19,14 @@ type (
)

// Validate applies a Ruleset to all SecurityGroups
func (l AWSResourceLinter) Validate(filenames []string, ruleSet assertion.RuleSet, tags []string, ruleIDs []string) ([]string, []assertion.ScannedResource, []assertion.Violation, error) {
noFilenames := []string{}
noScannedResources := []assertion.ScannedResource{}
func (l AWSResourceLinter) Validate(filenames []string, ruleSet assertion.RuleSet, tags []string, ruleIDs []string) (assertion.ValidationReport, error) {
rules := assertion.FilterRulesByTagAndID(ruleSet.Rules, tags, ruleIDs)
resources, err := l.Loader.Load()
if err != nil {
return noFilenames, noScannedResources, []assertion.Violation{}, err
return assertion.ValidationReport{}, err
}
r := ResourceLinter{Log: l.Log}
scannedResources, violations, err := r.ValidateResources(resources, rules)
return noFilenames, scannedResources, violations, err
return r.ValidateResources(resources, rules)
}

// Search applies a JMESPath to all SecurityGroups
Expand Down
8 changes: 8 additions & 0 deletions cli/common.go
Expand Up @@ -31,3 +31,11 @@ func getResourceIDFromFilename(filename string) string {
_, resourceID := filepath.Split(filename)
return resourceID
}

func combineValidationReports(r1, r2 assertion.ValidationReport) assertion.ValidationReport {
return assertion.ValidationReport{
FilesScanned: append(r1.FilesScanned, r2.FilesScanned...),
ResourcesScanned: append(r1.ResourcesScanned, r2.ResourcesScanned...),
Violations: append(r1.Violations, r2.Violations...),
}
}
24 changes: 13 additions & 11 deletions cli/file_linter.go
Expand Up @@ -11,30 +11,32 @@ type FileLinter struct {
}

// ValidateFiles validates a collection of filenames using a RuleSet
func (l FileLinter) ValidateFiles(filenames []string, ruleSet assertion.RuleSet, tags []string, ruleIDs []string, loader ResourceLoader) ([]string, []assertion.ScannedResource, []assertion.Violation, error) {
func (l FileLinter) ValidateFiles(filenames []string, ruleSet assertion.RuleSet, tags []string, ruleIDs []string, loader ResourceLoader) (assertion.ValidationReport, error) {

report := assertion.ValidationReport{
FilesScanned: []string{},
ResourcesScanned: []assertion.ScannedResource{},
Violations: []assertion.Violation{},
}
rules := assertion.FilterRulesByTagAndID(ruleSet.Rules, tags, ruleIDs)
allViolations := make([]assertion.Violation, 0)
filesScanned := make([]string, 0)
resourcesScanned := make([]assertion.ScannedResource, 0)
r := ResourceLinter{Log: l.Log}
for _, filename := range filenames {
include, err := assertion.ShouldIncludeFile(ruleSet.Files, filename)
if err == nil && include {
l.Log(fmt.Sprintf("Processing %s", filename))
resources, err := loader.Load(filename)
if err != nil {
return filesScanned, resourcesScanned, allViolations, err
return report, err
}
scanned, violations, err := r.ValidateResources(resources, rules)
resourcesScanned = append(resourcesScanned, scanned...)
r, err := r.ValidateResources(resources, rules)
r.FilesScanned = []string{filename}
report = combineValidationReports(report, r)
if err != nil {
return filesScanned, resourcesScanned, allViolations, err
return report, err
}
allViolations = append(allViolations, violations...)
filesScanned = append(filesScanned, filename)
}
}
return filesScanned, resourcesScanned, allViolations, nil
return report, nil
}

// SearchFiles evaluates a JMESPath expression against resources in a collection of filenames
Expand Down
2 changes: 1 addition & 1 deletion cli/kubernetes.go
Expand Up @@ -50,7 +50,7 @@ func (l KubernetesResourceLoader) Load(filename string) ([]assertion.Resource, e
}

// Validate runs validate on a collection of filenames using a RuleSet
func (l KubernetesLinter) Validate(filenames []string, ruleSet assertion.RuleSet, tags []string, ruleIDs []string) ([]string, []assertion.ScannedResource, []assertion.Violation, error) {
func (l KubernetesLinter) Validate(filenames []string, ruleSet assertion.RuleSet, tags []string, ruleIDs []string) (assertion.ValidationReport, error) {
loader := KubernetesResourceLoader{Log: l.Log}
f := FileLinter{Log: l.Log}
return f.ValidateFiles(filenames, ruleSet, tags, ruleIDs, loader)
Expand Down
2 changes: 1 addition & 1 deletion cli/linter.go
Expand Up @@ -9,7 +9,7 @@ import (

// Linter provides the interface for all supported linters
type Linter interface {
Validate(filenames []string, ruleSet assertion.RuleSet, tags []string, ruleIDs []string) ([]string, []assertion.ScannedResource, []assertion.Violation, error)
Validate(filenames []string, ruleSet assertion.RuleSet, tags []string, ruleIDs []string) (assertion.ValidationReport, error)
Search(filenames []string, ruleSet assertion.RuleSet, searchExpression string)
}

Expand Down
16 changes: 9 additions & 7 deletions cli/resource_linter.go
Expand Up @@ -11,15 +11,17 @@ type ResourceLinter struct {
}

// ValidateResources evaluates a list of Rule objects to a list of Resource objects
func (r ResourceLinter) ValidateResources(resources []assertion.Resource, rules []assertion.Rule) ([]assertion.ScannedResource, []assertion.Violation, error) {
func (r ResourceLinter) ValidateResources(resources []assertion.Resource, rules []assertion.Rule) (assertion.ValidationReport, error) {

scannedResources := make([]assertion.ScannedResource, 0)
report := assertion.ValidationReport{
ResourcesScanned: []assertion.ScannedResource{},
Violations: []assertion.Violation{},
}

valueSource := assertion.StandardValueSource{Log: r.Log}
resolvedRules := assertion.ResolveRules(rules, valueSource, r.Log)
externalRules := assertion.StandardExternalRuleInvoker{Log: r.Log}

allViolations := make([]assertion.Violation, 0)
for _, rule := range resolvedRules {
r.Log(fmt.Sprintf("Rule %s: %s", rule.ID, rule.Message))
for _, resource := range assertion.FilterResourcesByType(resources, rule.Resource) {
Expand All @@ -28,16 +30,16 @@ func (r ResourceLinter) ValidateResources(resources []assertion.Resource, rules
} else {
status, violations, err := assertion.CheckRule(rule, resource, externalRules, r.Log)
if err != nil {
return scannedResources, allViolations, err
return report, nil
}
scannedResources = append(scannedResources, assertion.ScannedResource{
report.ResourcesScanned = append(report.ResourcesScanned, assertion.ScannedResource{
ResourceID: resource.ID,
ResourceType: resource.Type,
Status: status,
})
allViolations = append(allViolations, violations...)
report.Violations = append(report.Violations, violations...)
}
}
}
return scannedResources, allViolations, nil
return report, nil
}
2 changes: 1 addition & 1 deletion cli/rules_linter.go
Expand Up @@ -58,7 +58,7 @@ func (l RulesResourceLoader) Load(filename string) ([]assertion.Resource, error)
}

// Validate runs validate on a collection of filenames using a RuleSet
func (l RulesLinter) Validate(filenames []string, ruleSet assertion.RuleSet, tags []string, ruleIDs []string) ([]string, []assertion.ScannedResource, []assertion.Violation, error) {
func (l RulesLinter) Validate(filenames []string, ruleSet assertion.RuleSet, tags []string, ruleIDs []string) (assertion.ValidationReport, error) {
loader := RulesResourceLoader{Log: l.Log}
f := FileLinter{Log: l.Log}
return f.ValidateFiles(filenames, ruleSet, tags, ruleIDs, loader)
Expand Down
2 changes: 1 addition & 1 deletion cli/terraform.go
Expand Up @@ -102,7 +102,7 @@ func (l TerraformResourceLoader) Load(filename string) ([]assertion.Resource, er
}

// Validate uses a RuleSet to validate resources in a collection of Terraform configuration files
func (l TerraformLinter) Validate(filenames []string, ruleSet assertion.RuleSet, tags []string, ruleIDs []string) ([]string, []assertion.ScannedResource, []assertion.Violation, error) {
func (l TerraformLinter) Validate(filenames []string, ruleSet assertion.RuleSet, tags []string, ruleIDs []string) (assertion.ValidationReport, error) {
loader := TerraformResourceLoader{Log: l.Log}
f := FileLinter{Log: l.Log}
return f.ValidateFiles(filenames, ruleSet, tags, ruleIDs, loader)
Expand Down
14 changes: 7 additions & 7 deletions cli/terraform_test.go
Expand Up @@ -10,17 +10,17 @@ func TestTerraformLinter(t *testing.T) {
linter := TerraformLinter{Log: testLogger}
ruleSet := loadRulesForTest("./testdata/rules/terraform_instance.yml", t)
filenames := []string{"./testdata/resources/terraform_instance.tf"}
filesScanned, resourcesScanned, violations, err := linter.Validate(filenames, ruleSet, emptyTags, emptyIds)
report, err := linter.Validate(filenames, ruleSet, emptyTags, emptyIds)
if err != nil {
t.Error("Expecting TestTerraformLinter to not return an error")
}
if len(resourcesScanned) != 1 {
t.Errorf("TestTerraformLinter scanned %d resources, expecting 1", len(resourcesScanned))
if len(report.ResourcesScanned) != 1 {
t.Errorf("TestTerraformLinter scanned %d resources, expecting 1", len(report.ResourcesScanned))
}
if len(filesScanned) != 1 {
t.Errorf("TestTerraformLinter scanned %d files, expecting 1", len(filesScanned))
if len(report.FilesScanned) != 1 {
t.Errorf("TestTerraformLinter scanned %d files, expecting 1", len(report.FilesScanned))
}
if len(violations) != 0 {
t.Errorf("TestTerraformLinter returned %d violations, expecting 0", len(violations))
if len(report.Violations) != 0 {
t.Errorf("TestTerraformLinter returned %d violations, expecting 0", len(report.Violations))
}
}
2 changes: 1 addition & 1 deletion cli/yaml_linter.go
Expand Up @@ -54,7 +54,7 @@ func (l YAMLResourceLoader) Load(filename string) ([]assertion.Resource, error)
}

// Validate runs validate on a collection of filenames using a RuleSet
func (l YAMLLinter) Validate(filenames []string, ruleSet assertion.RuleSet, tags []string, ruleIDs []string) ([]string, []assertion.ScannedResource, []assertion.Violation, error) {
func (l YAMLLinter) Validate(filenames []string, ruleSet assertion.RuleSet, tags []string, ruleIDs []string) (assertion.ValidationReport, error) {
loader := YAMLResourceLoader{Log: l.Log, Resources: ruleSet.Resources}
f := FileLinter{Log: l.Log}
return f.ValidateFiles(filenames, ruleSet, tags, ruleIDs, loader)
Expand Down
14 changes: 7 additions & 7 deletions cli/yaml_linter_test.go
Expand Up @@ -10,17 +10,17 @@ func TestYAMLLinter(t *testing.T) {
linter := YAMLLinter{Log: testLogger}
ruleSet := loadRulesForTest("./testdata/rules/generic.yml", t)
filenames := []string{"./testdata/resources/generic.config"}
filesScanned, resourcesScanned, violations, err := linter.Validate(filenames, ruleSet, emptyTags, emptyIds)
report, err := linter.Validate(filenames, ruleSet, emptyTags, emptyIds)
if err != nil {
t.Error("Expecting TestYAMLLinter to not return an error")
}
if len(resourcesScanned) != 17 {
t.Errorf("TestTerraformLinter scanned %d resources, expecting 17", len(resourcesScanned))
if len(report.ResourcesScanned) != 17 {
t.Errorf("TestTerraformLinter scanned %d resources, expecting 17", len(report.ResourcesScanned))
}
if len(filesScanned) != 1 {
t.Errorf("TestYAMLLinter scanned %d files, expecting 1", len(filesScanned))
if len(report.FilesScanned) != 1 {
t.Errorf("TestYAMLLinter scanned %d files, expecting 1", len(report.FilesScanned))
}
if len(violations) != 3 {
t.Errorf("TestYAMLLinter returned %d violations, expecting 3", len(violations))
if len(report.Violations) != 3 {
t.Errorf("TestYAMLLinter returned %d violations, expecting 3", len(report.Violations))
}
}

0 comments on commit e02be90

Please sign in to comment.