Skip to content

feat: add go symbol matching qualifier for better go vuln matching#3509

Open
spiffcs wants to merge 22 commits into
mainfrom
go-symbol-matching
Open

feat: add go symbol matching qualifier for better go vuln matching#3509
spiffcs wants to merge 22 commits into
mainfrom
go-symbol-matching

Conversation

@spiffcs

@spiffcs spiffcs commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Go symbol matching: stdlib and golang.org/x advisories match correctly based on new symbol data; merge govulndb symbol data onto GHSA records

The [Go Vuln DB] (https://vuln.go.dev/) is the only source for Go standard library advisories. It is also an authoritative source for a share of golang.org/x/* advisories.

Grype recently started hosting GoVulnDB records in its DB (#3527, #3534, #3527).

Keeping these records exposed without symbol matching lead to matching inaccuracies. We also would see inaccuracies from cases in GHSA where we did not have symbol data and only matched at the module/version range level.

PR Features

  • stdlib and golang.org/x/* records now match on symbols
  • grype catalogs Go binary function symbols by default (syft symbol capture enabled in getProviderConfig),
  • govulndb's per-symbol reachability is stored as a go_imports qualifier, and only matches when a vulnerable symbol is actually present in the binary
  • Stdlib packages always stay on the GO records (nothing else covers them);
  • x/* advisories without a GHSA counterpart are also kept.
  • Packages without symbol evidence (pre-built SBOMs) keep today's module-granularity behavior

Data Decisions

Where a GHSA covers the same module, the GHSA becomes the single, symbol-enriched record.

  • Each GHSA affected package matching a GO record's module is patched with the go-imports qualifier.
  • Patching is decided per exact match on affected[].package.name
  • The GO record is written only if packages remain that were NOT found in the GHSA.
  • Ranges: the GHSA's win by default
  • Ranges exception: a GHSA range pinned to the pseudo-version of the fix commit can never match a real tagged release (v5.21.1 sorts above v0.0.0-… — a guaranteed FN). When the GO record carries that exact pseudo-version in its standard range plus exactly one custom range in ecosystem_specific.custom_ranges we patch in the custom_range
  • Every GHSA amendment is recorded on the blob's modifications audit trail ({url, changes[]) changes can be symbol additions and range replacements.

Schema:

  • DB model 6.1.9 adds GoImports to package qualifiers
  • db search 1.1.7 adds go_imports
  • db search vuln 1.0.6 adds modifications

Known limitations / follow-ups (tracked separately)

  • Non-semver version strings (~20 govulndb custom_ranges strings plus 14 + 45 on the GHSA side

@oss-housekeeper oss-housekeeper Bot added the dependencies dealing with project dependencies label Jun 17, 2026
@spiffcs spiffcs marked this pull request as draft June 17, 2026 14:21
@spiffcs spiffcs force-pushed the go-symbol-matching branch from f11bb65 to 896f135 Compare July 1, 2026 04:04
spiffcs added 2 commits July 1, 2026 00:25
govulndb's per-symbol reachability information
used as a go-imports package qualifier

called at match time to reduce false positives for Go binaries.

When a Go binary is cataloged with symbol capture enabled, the qualifier
only matches a vulnerability if at least one of the vulnerable symbols is
actually present in the binary. Packages without symbol evidence continue
to match at module granularity, preserving existing behavior.

Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
@spiffcs spiffcs force-pushed the go-symbol-matching branch from a63589f to b621b65 Compare July 1, 2026 04:25
spiffcs and others added 6 commits July 1, 2026 02:31
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
@spiffcs spiffcs marked this pull request as ready for review July 6, 2026 15:48
@spiffcs spiffcs marked this pull request as draft July 6, 2026 16:14
spiffcs added 5 commits July 6, 2026 12:29
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
* origin/main:
  chore(deps): bump gorm.io/gorm from 1.31.1 to 1.31.2 (#3557)
  chore(deps): bump actions/cache in /.github/actions/bootstrap (#3558)
  chore(deps): bump anchore/go-make/.github/actions/setup (#3555)
  chore(deps): bump actions/cache from 5.0.5 to 6.1.0 (#3551)
  chore(deps): bump actions/cache/restore in /.github/actions/bootstrap (#3556)
  chore(deps): bump golang.org/x/tools from 0.46.0 to 0.47.0 (#3550)
  chore(deps): bump anchore/workflows/.github/workflows/codeql.yaml (#3548)
  chore(deps): bump zizmorcore/zizmor-action from 0.5.6 to 0.5.7 (#3549)
  chore(deps): bump anchore/workflows/.github/workflows/check-version-available.yaml (#3547)
  fix(zarf): swap warn messages for debug (#3545)
  feat: populate package architecture for matching (#3504)
  chore(deps): update quality gate database (#3543)
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
@spiffcs spiffcs marked this pull request as ready for review July 7, 2026 03:38
@spiffcs spiffcs requested a review from wagoodman July 7, 2026 03:38
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Comment thread grype/db/v6/blobs.go
// 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?

Comment thread grype/db/v6/build/writer.go Outdated
var added []db.GoImport
for _, imp := range imports {
if existing[imp.Path] {
continue

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I doubt this is likely, but I'll mention it anyway: if there are two go records aliasing the same GHSA that list the same import path with different symbol sets then the second set is silently dropped instead of being combined.

Comment on lines +124 to +128
// writeEntryToBatch fills in missing severity and adds the entry to the write
// batches. Held govulndb/GHSA entries also come back through here at Close
// time, after reconciliation (see govulndb_merge.go), so severity fill always
// runs with the complete NVD cache.
func (w *writer) writeEntryToBatch(entry transformers.RelatedEntries) error {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why hold all of the go and ghsa records? We have a mechanism for ordering providers one before another (today we have NVD before others). I feel that we could write ghsa's as usual then patch overlapping ghsa records when we write the go vuln records --which doesn't requiring holding any/all records in memory and write them at close.

I did not look at the *provider caches that we have and how they are affected.

Was there specific frictions/reasons why to hold writes for all records?

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.

We talked about this on a sync, but to record the decision here:

  • We're not holding all the ghsa records, just the ones from the Go ecosystem
  • Write once was deemed a better path than write then patch because of the additional complexity of adding new patch code.

If we run into issues with write once we can come back on a follow up to add the PATCH methods to the db builder/reconciler

Comment thread grype/db/v6/build/transformers/osv/testdata/GO-2026-4610.json Outdated
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
if !govulndbEmits(affected.Package.Name) {
continue
var qualifiers *db.PackageQualifiers
if imports := govulndbImports(affected, vuln.ID); len(imports) > 0 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is it worth also capturing an architecture qualifier, e.g. https://vuln.go.dev/ID/GO-2022-0187.json is only affected on amd64. That's probably out of scope here but i wanted to check what others think. My vote would be to add it as a fast follow but still wanted to mention it.

spiffcs and others added 4 commits July 8, 2026 13:05
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
spiffcs added 3 commits July 8, 2026 21:23
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
govulndb fix versions source from ecosystem_specific.custom_ranges
withdrawn records written without GHSA reconciliation (NO GHSA) reach the writer with a
fixed range but no availability dates

This fails failOnMissingFixDate builds:
(e.g. GO-2024-2442 / github.com/gravitational/teleport:
`missing fix date for version "13.4.13"`).

This fix mirrors the upstream providers' fallback
we stamp the fix date as the advisory's own published date
(then modified) with kind "first-observed-record" on any reconciled go record
whose fixed range lacks one.

Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies dealing with project dependencies

Projects

None yet

3 participants