diff --git a/pkg/iac/ignore/parse.go b/pkg/iac/ignore/parse.go index bb2f13603263..889848907548 100644 --- a/pkg/iac/ignore/parse.go +++ b/pkg/iac/ignore/parse.go @@ -5,6 +5,8 @@ import ( "strings" "time" + "github.com/samber/lo" + "github.com/aquasecurity/trivy/pkg/iac/types" "github.com/aquasecurity/trivy/pkg/log" ) @@ -36,23 +38,34 @@ func Parse(src, path string, parsers ...RuleSectionParser) Rules { func parseLine(line string, rng types.Range, parsers []RuleSectionParser) []Rule { var rules []Rule - sections := strings.Split(strings.TrimSpace(line), " ") - for _, section := range sections { - section := strings.TrimSpace(section) - section = strings.TrimLeftFunc(section, func(r rune) bool { + parts := strings.Split(strings.TrimSpace(line), " ") + parts = lo.FilterMap(parts, func(part string, _ int) (string, bool) { + part = strings.TrimSpace(part) + part = strings.TrimLeftFunc(part, func(r rune) bool { return r == '#' || r == '/' || r == '*' }) - section, exists := hasIgnoreRulePrefix(section) + return part, part != "" + }) + + for i, part := range parts { + part, exists := hasIgnoreRulePrefix(part) if !exists { continue } - rule, err := parseComment(section, rng, parsers) + sections, err := parseRuleSections(part, rng, parsers) if err != nil { log.Debug("Failed to parse rule", log.String("range", rng.String()), log.Err(err)) continue } + + rule := Rule{ + rng: rng, + isStartLine: i == 0 || (len(rules) > 0 && rules[0].isStartLine), + sections: sections, + } + rules = append(rules, rule) } @@ -72,11 +85,8 @@ func hasIgnoreRulePrefix(s string) (string, bool) { return "", false } -func parseComment(input string, rng types.Range, parsers []RuleSectionParser) (Rule, error) { - rule := Rule{ - rng: rng, - sections: make(map[string]any), - } +func parseRuleSections(input string, rng types.Range, parsers []RuleSectionParser) (map[string]any, error) { + sections := make(map[string]any) parsers = append(parsers, &expiryDateParser{ rng: rng, @@ -93,7 +103,7 @@ func parseComment(input string, rng types.Range, parsers []RuleSectionParser) (R StringMatchParser{SectionKey: "id"}, } if idParser.Parse(val) { - rule.sections[idParser.Key()] = idParser.Param() + sections[idParser.Key()] = idParser.Param() } } @@ -103,16 +113,16 @@ func parseComment(input string, rng types.Range, parsers []RuleSectionParser) (R } if parser.Parse(val) { - rule.sections[parser.Key()] = parser.Param() + sections[parser.Key()] = parser.Param() } } } - if _, exists := rule.sections["id"]; !exists { - return Rule{}, errors.New("rule section with the `ignore` key is required") + if _, exists := sections["id"]; !exists { + return nil, errors.New("rule section with the `ignore` key is required") } - return rule, nil + return sections, nil } type StringMatchParser struct { diff --git a/pkg/iac/ignore/rule.go b/pkg/iac/ignore/rule.go index 61057ce75f87..86e24e8dd120 100644 --- a/pkg/iac/ignore/rule.go +++ b/pkg/iac/ignore/rule.go @@ -30,11 +30,16 @@ func (r Rules) shift() { ) for i := len(r) - 1; i > 0; i-- { - currentIgnore, nextIgnore := r[i], r[i-1] + currentRule, prevRule := r[i], r[i-1] + + if !prevRule.isStartLine { + continue + } + if currentRange == nil { - currentRange = ¤tIgnore.rng + currentRange = ¤tRule.rng } - if nextIgnore.rng.GetStartLine()+1+offset == currentIgnore.rng.GetStartLine() { + if prevRule.rng.GetStartLine()+1+offset == currentRule.rng.GetStartLine() { r[i-1].rng = *currentRange offset++ } else { @@ -46,8 +51,9 @@ func (r Rules) shift() { // Rule represents a rule for ignoring vulnerabilities. type Rule struct { - rng types.Range - sections map[string]any + rng types.Range + isStartLine bool + sections map[string]any } func (r Rule) ignore(m types.Metadata, ids []string, ignorers map[string]Ignorer) bool { diff --git a/pkg/iac/ignore/rule_test.go b/pkg/iac/ignore/rule_test.go index da89ca2a3595..29c38bf76b5d 100644 --- a/pkg/iac/ignore/rule_test.go +++ b/pkg/iac/ignore/rule_test.go @@ -190,6 +190,50 @@ func TestRules_Ignore(t *testing.T) { }, shouldIgnore: false, }, + { + name: "multiple ignore rules on the same line", + src: `test #trivy:ignore:rule-1 +test #trivy:ignore:rule-2 + `, + args: args{ + metadata: metadataWithLine(filename, 1), + ids: []string{"rule-1"}, + }, + shouldIgnore: true, + }, + { + name: "multiple ignore rules on the same line", + src: `# trivy:ignore:rule-1 +# trivy:ignore:rule-2 +test #trivy:ignore:rule-3 +`, + args: args{ + metadata: metadataWithLine(filename, 3), + ids: []string{"rule-1"}, + }, + shouldIgnore: true, + }, + { + name: "multiple ignore rules on the same line", + src: `# trivy:ignore:rule-1 # trivy:ignore:rule-2 +# trivy:ignore:rule-3 +test #trivy:ignore:rule-4 +`, + args: args{ + metadata: metadataWithLine(filename, 3), + ids: []string{"rule-2"}, + }, + shouldIgnore: true, + }, + { + name: "multiple ids", + src: `# trivy:ignore:rule-1`, + args: args{ + metadata: metadataWithLine(filename, 1), + ids: []string{"rule-1", "rule-2"}, + }, + shouldIgnore: true, + }, } for _, tt := range tests {