Skip to content

Commit

Permalink
fix: adds ignore rules for kernel-headers indirect matches (#1787)
Browse files Browse the repository at this point in the history
* fix: adds ignore rules for kernel-headers indirect matches

Adds ignoring of kernel-headers indirect matches on kernel vulns
since the kernel-headers package does not have the kernel code in it
that kernel vulns are actually referring to.

Adds a config value to control this ignore behavior that defaults to
enabling the ignore rules.

Fixes: 1762

* Adds ignore rule support for match types and upstream package names.
* Adds default ignore rules for kernel-headers indirect matches on kernel
for rpms.

Signed-off-by: Zach Hill <zach@anchore.com>

* chore: add match-upstream-kernel-headers config to README.md

Signed-off-by: Zach Hill <zach@anchore.com>

* chore: update match labels

Signed-off-by: Keith Zantow <kzantow@gmail.com>

---------

Signed-off-by: Zach Hill <zach@anchore.com>
Signed-off-by: Keith Zantow <kzantow@gmail.com>
Co-authored-by: Keith Zantow <kzantow@gmail.com>
  • Loading branch information
zhill and kzantow committed Apr 15, 2024
1 parent 018b415 commit a7cbe3a
Show file tree
Hide file tree
Showing 7 changed files with 378 additions and 44 deletions.
4 changes: 4 additions & 0 deletions README.md
Expand Up @@ -703,6 +703,10 @@ file: ""
# same as --exclude ; GRYPE_EXCLUDE env var
exclude: []

# include matches on kernel-headers packages that are matched against upstream kernel package
# if 'false' any such matches are marked as ignored
match-upstream-kernel-headers: false

# os and/or architecture to use when referencing container images (e.g. "windows/armv6" or "arm64")
# same as --platform; GRYPE_PLATFORM env var
platform: ""
Expand Down
8 changes: 8 additions & 0 deletions cmd/grype/cli/commands/root.go
Expand Up @@ -99,6 +99,10 @@ var ignoreVEXFixedNotAffected = []match.IgnoreRule{
{VexStatus: string(vex.StatusFixed)},
}

var ignoreLinuxKernelHeaders = []match.IgnoreRule{
{Package: match.IgnoreRulePackage{Name: "kernel-headers", UpstreamName: "kernel", Type: "rpm"}, MatchType: match.ExactIndirectMatch},
}

//nolint:funlen
func runGrype(app clio.Application, opts *options.Grype, userInput string) (errs error) {
writer, err := format.MakeScanResultWriter(opts.Outputs, opts.File, format.PresentationConfig{
Expand All @@ -124,6 +128,10 @@ func runGrype(app clio.Application, opts *options.Grype, userInput string) (errs
opts.Ignore = append(opts.Ignore, ignoreFixedMatches...)
}

if !opts.MatchUpstreamKernelHeaders {
opts.Ignore = append(opts.Ignore, ignoreLinuxKernelHeaders...)
}

for _, ignoreState := range stringutil.SplitCommaSeparatedString(opts.IgnoreStates) {
switch grypeDb.FixState(ignoreState) {
case grypeDb.UnknownFixState, grypeDb.FixedState, grypeDb.NotFixedState, grypeDb.WontFixState:
Expand Down
62 changes: 32 additions & 30 deletions cmd/grype/cli/options/grype.go
Expand Up @@ -11,30 +11,31 @@ import (
)

type Grype struct {
Outputs []string `yaml:"output" json:"output" mapstructure:"output"` // -o, <presenter>=<file> the Presenter hint string to use for report formatting and the output file
File string `yaml:"file" json:"file" mapstructure:"file"` // --file, the file to write report output to
Distro string `yaml:"distro" json:"distro" mapstructure:"distro"` // --distro, specify a distro to explicitly use
GenerateMissingCPEs bool `yaml:"add-cpes-if-none" json:"add-cpes-if-none" mapstructure:"add-cpes-if-none"` // --add-cpes-if-none, automatically generate CPEs if they are not present in import (e.g. from a 3rd party SPDX document)
OutputTemplateFile string `yaml:"output-template-file" json:"output-template-file" mapstructure:"output-template-file"` // -t, the template file to use for formatting the final report
CheckForAppUpdate bool `yaml:"check-for-app-update" json:"check-for-app-update" mapstructure:"check-for-app-update"` // whether to check for an application update on start up or not
OnlyFixed bool `yaml:"only-fixed" json:"only-fixed" mapstructure:"only-fixed"` // only fail if detected vulns have a fix
OnlyNotFixed bool `yaml:"only-notfixed" json:"only-notfixed" mapstructure:"only-notfixed"` // only fail if detected vulns don't have a fix
IgnoreStates string `yaml:"ignore-states" json:"ignore-wontfix" mapstructure:"ignore-wontfix"` // ignore detections for vulnerabilities matching these comma-separated fix states
Platform string `yaml:"platform" json:"platform" mapstructure:"platform"` // --platform, override the target platform for a container image
Search search `yaml:"search" json:"search" mapstructure:"search"`
Ignore []match.IgnoreRule `yaml:"ignore" json:"ignore" mapstructure:"ignore"`
Exclusions []string `yaml:"exclude" json:"exclude" mapstructure:"exclude"`
DB Database `yaml:"db" json:"db" mapstructure:"db"`
ExternalSources externalSources `yaml:"external-sources" json:"externalSources" mapstructure:"external-sources"`
Match matchConfig `yaml:"match" json:"match" mapstructure:"match"`
FailOn string `yaml:"fail-on-severity" json:"fail-on-severity" mapstructure:"fail-on-severity"`
Registry registry `yaml:"registry" json:"registry" mapstructure:"registry"`
ShowSuppressed bool `yaml:"show-suppressed" json:"show-suppressed" mapstructure:"show-suppressed"`
ByCVE bool `yaml:"by-cve" json:"by-cve" mapstructure:"by-cve"` // --by-cve, indicates if the original match vulnerability IDs should be preserved or the CVE should be used instead
Name string `yaml:"name" json:"name" mapstructure:"name"`
DefaultImagePullSource string `yaml:"default-image-pull-source" json:"default-image-pull-source" mapstructure:"default-image-pull-source"`
VexDocuments []string `yaml:"vex-documents" json:"vex-documents" mapstructure:"vex-documents"`
VexAdd []string `yaml:"vex-add" json:"vex-add" mapstructure:"vex-add"` // GRYPE_VEX_ADD
Outputs []string `yaml:"output" json:"output" mapstructure:"output"` // -o, <presenter>=<file> the Presenter hint string to use for report formatting and the output file
File string `yaml:"file" json:"file" mapstructure:"file"` // --file, the file to write report output to
Distro string `yaml:"distro" json:"distro" mapstructure:"distro"` // --distro, specify a distro to explicitly use
GenerateMissingCPEs bool `yaml:"add-cpes-if-none" json:"add-cpes-if-none" mapstructure:"add-cpes-if-none"` // --add-cpes-if-none, automatically generate CPEs if they are not present in import (e.g. from a 3rd party SPDX document)
OutputTemplateFile string `yaml:"output-template-file" json:"output-template-file" mapstructure:"output-template-file"` // -t, the template file to use for formatting the final report
CheckForAppUpdate bool `yaml:"check-for-app-update" json:"check-for-app-update" mapstructure:"check-for-app-update"` // whether to check for an application update on start up or not
OnlyFixed bool `yaml:"only-fixed" json:"only-fixed" mapstructure:"only-fixed"` // only fail if detected vulns have a fix
OnlyNotFixed bool `yaml:"only-notfixed" json:"only-notfixed" mapstructure:"only-notfixed"` // only fail if detected vulns don't have a fix
IgnoreStates string `yaml:"ignore-states" json:"ignore-wontfix" mapstructure:"ignore-wontfix"` // ignore detections for vulnerabilities matching these comma-separated fix states
Platform string `yaml:"platform" json:"platform" mapstructure:"platform"` // --platform, override the target platform for a container image
Search search `yaml:"search" json:"search" mapstructure:"search"`
Ignore []match.IgnoreRule `yaml:"ignore" json:"ignore" mapstructure:"ignore"`
Exclusions []string `yaml:"exclude" json:"exclude" mapstructure:"exclude"`
DB Database `yaml:"db" json:"db" mapstructure:"db"`
ExternalSources externalSources `yaml:"external-sources" json:"externalSources" mapstructure:"external-sources"`
Match matchConfig `yaml:"match" json:"match" mapstructure:"match"`
FailOn string `yaml:"fail-on-severity" json:"fail-on-severity" mapstructure:"fail-on-severity"`
Registry registry `yaml:"registry" json:"registry" mapstructure:"registry"`
ShowSuppressed bool `yaml:"show-suppressed" json:"show-suppressed" mapstructure:"show-suppressed"`
ByCVE bool `yaml:"by-cve" json:"by-cve" mapstructure:"by-cve"` // --by-cve, indicates if the original match vulnerability IDs should be preserved or the CVE should be used instead
Name string `yaml:"name" json:"name" mapstructure:"name"`
DefaultImagePullSource string `yaml:"default-image-pull-source" json:"default-image-pull-source" mapstructure:"default-image-pull-source"`
VexDocuments []string `yaml:"vex-documents" json:"vex-documents" mapstructure:"vex-documents"`
VexAdd []string `yaml:"vex-add" json:"vex-add" mapstructure:"vex-add"` // GRYPE_VEX_ADD
MatchUpstreamKernelHeaders bool `yaml:"match-upstream-kernel-headers" json:"match-upstream-kernel-headers" mapstructure:"match-upstream-kernel-headers"` // Show matches on kernel-headers packages where the match is on kernel upstream instead of marking them as ignored, default=false
}

var _ interface {
Expand All @@ -44,12 +45,13 @@ var _ interface {

func DefaultGrype(id clio.Identification) *Grype {
return &Grype{
Search: defaultSearch(source.SquashedScope),
DB: DefaultDatabase(id),
Match: defaultMatchConfig(),
ExternalSources: defaultExternalSources(),
CheckForAppUpdate: true,
VexAdd: []string{},
Search: defaultSearch(source.SquashedScope),
DB: DefaultDatabase(id),
Match: defaultMatchConfig(),
ExternalSources: defaultExternalSources(),
CheckForAppUpdate: true,
VexAdd: []string{},
MatchUpstreamKernelHeaders: false,
}
}

Expand Down
41 changes: 36 additions & 5 deletions grype/match/ignore.go
Expand Up @@ -24,15 +24,17 @@ type IgnoreRule struct {
Package IgnoreRulePackage `yaml:"package" json:"package" mapstructure:"package"`
VexStatus string `yaml:"vex-status" json:"vex-status" mapstructure:"vex-status"`
VexJustification string `yaml:"vex-justification" json:"vex-justification" mapstructure:"vex-justification"`
MatchType Type `yaml:"match-type" json:"match-type" mapstructure:"match-type"`
}

// IgnoreRulePackage describes the Package-specific fields that comprise the IgnoreRule.
type IgnoreRulePackage struct {
Name string `yaml:"name" json:"name" mapstructure:"name"`
Version string `yaml:"version" json:"version" mapstructure:"version"`
Language string `yaml:"language" json:"language" mapstructure:"language"`
Type string `yaml:"type" json:"type" mapstructure:"type"`
Location string `yaml:"location" json:"location" mapstructure:"location"`
Name string `yaml:"name" json:"name" mapstructure:"name"`
Version string `yaml:"version" json:"version" mapstructure:"version"`
Language string `yaml:"language" json:"language" mapstructure:"language"`
Type string `yaml:"type" json:"type" mapstructure:"type"`
Location string `yaml:"location" json:"location" mapstructure:"location"`
UpstreamName string `yaml:"upstream-name" json:"upstream-name" mapstructure:"upstream-name"`
}

// ApplyIgnoreRules iterates through the provided matches and, for each match,
Expand Down Expand Up @@ -137,6 +139,13 @@ func getIgnoreConditionsForRule(rule IgnoreRule) []ignoreCondition {
ignoreConditions = append(ignoreConditions, ifFixStateApplies(fs))
}

if upstreamName := rule.Package.UpstreamName; upstreamName != "" {
ignoreConditions = append(ignoreConditions, ifUpstreamPackageNameApplies(upstreamName))
}

if matchType := rule.MatchType; matchType != "" {
ignoreConditions = append(ignoreConditions, ifMatchTypeApplies(matchType))
}
return ignoreConditions
}

Expand Down Expand Up @@ -188,6 +197,28 @@ func ifPackageLocationApplies(location string) ignoreCondition {
}
}

func ifUpstreamPackageNameApplies(name string) ignoreCondition {
return func(match Match) bool {
for _, upstream := range match.Package.Upstreams {
if name == upstream.Name {
return true
}
}
return false
}
}

func ifMatchTypeApplies(matchType Type) ignoreCondition {
return func(match Match) bool {
for _, mType := range match.Details.Types() {
if mType == matchType {
return true
}
}
return false
}
}

func ruleLocationAppliesToMatch(location string, match Match) bool {
for _, packageLocation := range match.Package.Locations.ToSlice() {
if ruleLocationAppliesToPath(location, packageLocation.RealPath) {
Expand Down

0 comments on commit a7cbe3a

Please sign in to comment.