Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions cmd/grype/cli/commands/internal/dbsearch/versions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package dbsearch

const (
// MatchesSchemaVersion is the schema version for the `db search` command
MatchesSchemaVersion = "1.1.6"
MatchesSchemaVersion = "1.1.7"

// MatchesSchemaVersion Changelog:
// 1.0.0 - Initial schema 🎉
Expand All @@ -16,14 +16,16 @@ const (
// 1.1.4 - Add rpm_arch field to PackageQualifiers (source/binary tagging for the CSAF VEX transformer)
// 1.1.5 - Add rootio field to PackageQualifiers (for Root IO NAK-pattern matching via the OSV rootio strategy)
// 1.1.6 - Rename rpm_arch field on PackageQualifiers to architecture (semantics unchanged; rpm-specific prefix dropped)
// 1.1.7 - Add go_imports field to PackageQualifiers (per-symbol reachability from govulndb ecosystem_specific.imports, used for Go binary symbol matching via the gosymbols qualifier)

// VulnerabilitiesSchemaVersion is the schema version for the `db search vuln` command
VulnerabilitiesSchemaVersion = "1.0.5"
VulnerabilitiesSchemaVersion = "1.0.6"

// VulnerabilitiesSchemaVersion
// 1.0.0 - Initial schema 🎉
// 1.0.1 - Add KEV and EPSS data to vulnerability
// 1.0.3 - Add severity string field to vulnerability object
// 1.0.4 - Add CWE IDs to vulnerability output
// 1.0.5 - Add ID field to Reference (for advisory IDs like RHSA-2023:5455)
// 1.0.6 - Add modifications field to the vulnerability object
)
37 changes: 37 additions & 0 deletions cmd/grype/cli/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import (
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/cataloging"
syftPkg "github.com/anchore/syft/syft/pkg"
syftGolang "github.com/anchore/syft/syft/pkg/cataloger/golang"
"github.com/anchore/syft/syft/sbom"
)

Expand Down Expand Up @@ -208,6 +209,7 @@ func runGrype(ctx context.Context, app clio.Application, opts *options.Grype, us
defer log.CloseAndLogError(vp, status.Path)

warnWhenDistroHintNeeded(packages, &pkgContext)
warnWhenGoSymbolsMissing(packages)

if err = applyVexRules(opts); err != nil {
return fmt.Errorf("applying vex rules: %w", err)
Expand Down Expand Up @@ -295,6 +297,35 @@ func warnWhenDistroHintNeeded(pkgs []pkg.Package, context *pkg.Context) {
}
}

// warnWhenGoSymbolsMissing alerts the user when Go binary packages were cataloged without function
// symbols. Grype captures symbols by default on its own scans (see getProviderConfig). This warning
// fires for pre-built SBOMs (e.g. `syft ... | grype`) generated without symbol capture. Without
// symbols Golang packages fall back to module-granularity matching and may surface false positives.
func warnWhenGoSymbolsMissing(pkgs []pkg.Package) {
if msg := goSymbolsMissingMessage(pkgs); msg != "" {
bus.Notify(msg)
}
}

// goSymbolsMissingMessage returns a warning for Go binary packages cataloged
// without function symbols, or "" when there is nothing to warn about.
func goSymbolsMissingMessage(pkgs []pkg.Package) string {
var withoutSymbols int
for _, p := range pkgs {
if m, ok := p.Metadata.(pkg.GolangBinMetadata); ok && len(m.Symbols) == 0 {
withoutSymbols++
}
}

if withoutSymbols == 0 {
return ""
}

return fmt.Sprintf("%d Go binary package(s) have no function symbols; matching falls back to module "+
"granularity and may report false positives. Regenerate the SBOM with symbol capture enabled "+
"(SYFT_GOLANG_CAPTURE_SYMBOLS=all syft ...) for more precise Go results.", withoutSymbols)
}

func warnDistroAlerts(data *models.DistroAlertData) {
if data == nil {
return
Expand Down Expand Up @@ -403,6 +434,12 @@ func getProviderConfig(opts *options.Grype) pkg.ProviderConfig {
cfg.Packages.JavaArchive.IncludeIndexedArchives = opts.Search.IncludeIndexedArchives
cfg.Packages.JavaArchive.IncludeUnindexedArchives = opts.Search.IncludeUnindexedArchives

// capture Go binary function symbols so the gosymbols qualifier can suppress false positives for
// module- and stdlib-scoped govulndb advisories (e.g. a net/http server DoS matching any binary that
// merely links net/http). Syft disables symbol capture by default, but for vulnerability matching the
// false-positive reduction is worth the extra catalog cost.
cfg.Packages.Golang = cfg.Packages.Golang.WithCaptureSymbols(syftGolang.SymbolScopeAll)

// when we run into a package with missing information like version, then this is not useful in the context
// of vulnerability matching. Though there will be downstream processing to handle this case, we can still
// save us the effort of ever attempting to match with these packages as early as possible.
Expand Down
38 changes: 38 additions & 0 deletions cmd/grype/cli/commands/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/cataloging"
"github.com/anchore/syft/syft/pkg/cataloger/binary"
syftGolang "github.com/anchore/syft/syft/pkg/cataloger/golang"
)

func Test_getProviderConfig(t *testing.T) {
Expand All @@ -49,6 +50,8 @@ func Test_getProviderConfig(t *testing.T) {
SBOMOptions: func() *syft.CreateSBOMConfig {
cfg := syft.DefaultCreateSBOMConfig()
cfg.Compliance.MissingVersion = cataloging.ComplianceActionDrop
// grype captures Go binary symbols by default; this is not syft's default
cfg.Packages.Golang = cfg.Packages.Golang.WithCaptureSymbols(syftGolang.SymbolScopeAll)
return cfg
}(),
RegistryOptions: &image.RegistryOptions{
Expand Down Expand Up @@ -319,3 +322,38 @@ func Test_applyVexRules(t *testing.T) {
})
}
}

func Test_goSymbolsMissingMessage(t *testing.T) {
goBinaryWithSymbols := pkg.Package{Metadata: pkg.GolangBinMetadata{Symbols: []string{"main.main"}}}
goBinaryWithoutSymbols := pkg.Package{Metadata: pkg.GolangBinMetadata{}}
nonGoPackage := pkg.Package{}

tests := []struct {
name string
pkgs []pkg.Package
want string
}{
{
name: "no packages",
},
{
name: "go binaries with symbols and non-go packages do not warn",
pkgs: []pkg.Package{goBinaryWithSymbols, nonGoPackage},
},
{
name: "go binaries without symbols warn with a count",
pkgs: []pkg.Package{goBinaryWithoutSymbols, goBinaryWithSymbols, goBinaryWithoutSymbols, nonGoPackage},
want: "2 Go binary package(s) have no function symbols",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := goSymbolsMissingMessage(tt.pkgs)
if tt.want == "" {
assert.Empty(t, got)
return
}
assert.Contains(t, got, tt.want)
})
}
}
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ require (
github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4
github.com/anchore/packageurl-go v0.2.0
github.com/anchore/stereoscope v0.2.2
github.com/anchore/syft v1.46.0
github.com/anchore/syft v1.46.1-0.20260701033749-a1831ce3b499
github.com/aquasecurity/go-pep440-version v0.0.1
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
github.com/bitnami/go-version v0.0.0-20250505154626-452e8c5ee607
Expand Down Expand Up @@ -339,6 +339,7 @@ require (
google.golang.org/grpc v1.80.0 // indirect
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
howett.net/plist v1.0.1 // indirect
modernc.org/libc v1.72.3 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
Expand Down
8 changes: 6 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@ github.com/anchore/packageurl-go v0.2.0 h1:CkrM4RMUwrEGAiE1OVlxaZNzWj0TuHRey7o4T
github.com/anchore/packageurl-go v0.2.0/go.mod h1:2JCgOQMIsqZ7TmliXG4PnUthPJAKE3mWQbsW2XHjAOE=
github.com/anchore/stereoscope v0.2.2 h1:SGTLGoF6GHmKEn9Bb6LYpzzgz9nhvMZgN/c4fTSQjYY=
github.com/anchore/stereoscope v0.2.2/go.mod h1:ylJXJKebLctP7u9ewguB1d0zUETJxvAA7r2DiuljDTM=
github.com/anchore/syft v1.46.0 h1:qIXMOJMlznWsWgzOT6xzK8aDhpOG7wnkbA0BS2UKF0c=
github.com/anchore/syft v1.46.0/go.mod h1:LwBimjMV20nC1zAsN5jzpGTg/6f0X4Acvvx1WymA53g=
github.com/anchore/syft v1.46.1-0.20260701033749-a1831ce3b499 h1:gxKUd0qvSHyHe9/nW8oaG78tFwF7kHStmcQwYf99Kkc=
github.com/anchore/syft v1.46.1-0.20260701033749-a1831ce3b499/go.mod h1:D2EesMQiokuYW5w5y7tVM4pNYGsaYxqNIImffSSgGnY=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
Expand Down Expand Up @@ -651,6 +651,7 @@ github.com/invopop/jsonschema v0.14.0 h1:MHQqLhvpNUZfw+hM3AZDYK7jxO8FZoQeQM77g8i
github.com/invopop/jsonschema v0.14.0/go.mod h1:ygm6C2EaVNMBDPpaPlnOA2pFAxBnxGjFlMZABxm9n2I=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
Expand Down Expand Up @@ -1560,6 +1561,7 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Expand All @@ -1584,6 +1586,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
modernc.org/cc/v4 v4.28.2 h1:3tQ0lf2ADtoby2EtSP+J7IE2SHwEJdP8ioR59wx7XpY=
modernc.org/cc/v4 v4.28.2/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
modernc.org/ccgo/v4 v4.34.0 h1:yRLPFZieg532OT4rp4JFNIVcquwalMX26G95WQDqwCQ=
Expand Down
29 changes: 29 additions & 0 deletions grype/db/v6/blobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,26 @@ type VulnerabilityBlob struct {

// Severities is a list of severity indications (quantitative or qualitative) for the vulnerability
Severities []Severity `json:"severities,omitempty"`

// Modifications is an audit trail of build-time amendments made to this record from other data
// sources (e.g. a GHSA record patched with Go symbol information from the aliased govulndb record).
Modifications []Modification `json:"modifications,omitempty"`
}

func (v VulnerabilityBlob) String() string {
return v.ID
}

// Modification records a single build-time amendment to a vulnerability record: the URL of the
// data source the amendment was derived from and a description of each change made.
type Modification struct {
// URL points to the source data the record was modified with (e.g. "https://vuln.go.dev/ID/GO-2024-2687.json").
URL string `json:"url"`

// Changes describes each amendment made to the record (e.g. "added go symbols to affected package golang.org/x/net").
Changes []string `json:"changes,omitempty"`
}

// Reference represents a single external URL and string tags to use for organizational purposes
type Reference struct {
// URL is the external resource
Expand Down Expand Up @@ -160,6 +174,21 @@ type PackageQualifiers struct {
// RootIO indicates that the vulnerability applies only to Root IO packages (packages with Root IO fixes).
// When true, standard packages will not match this vulnerability (NAK pattern).
RootIO *bool `json:"rootio,omitempty"`

// GoImports lists the packages and symbols within an affected Go module that contain the vulnerability
// (from govulndb's ecosystem_specific.imports). When set, packages carrying binary symbol evidence only
// match if at least one of the listed symbols is present in the binary.
GoImports []GoImport `json:"go_imports,omitempty"`

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the grype report what of the symbol data should we end up including? Just what the intersection was that caused the match or the full table for the package?

}

// GoImport describes a single package within an affected Go module and the vulnerable symbols it contains.
type GoImport struct {
// Path is the import path of the package within the affected module (e.g. "golang.org/x/net/html").
Path string `json:"path"`

// Symbols lists the vulnerable function/method names within the package (e.g. "Parse" or "Decoder.Decode").
// An empty list means the entire package is considered vulnerable.
Symbols []string `json:"symbols,omitempty"`
}

// Range defines a specific range of package versions pertaining to a vulnerability.
Expand Down
Loading
Loading