mirror of
https://github.com/anchore/syft.git
synced 2026-08-19 08:38:25 +02:00
* feat(golang): add extended-stdlib scope and include patterns for symbol capture
`golang.capture-symbols` decides how much symbol data lands in the SBOM for grype's reachability analysis. It's `none`, `stdlib`, or `all` today, and the useful middle is missing: `stdlib` stops at the standard library, `all` multiplies SBOM size.
A new `extended-stdlib` configurable covers stdlib plus everything under `golang.org/x/`:
```yaml
golang:
capture-symbols: extended-stdlib
```
Also, a new `capture-symbols-include` configurable for modules that are noisy in your binaries but not everyone's. It's unioned with whatever the scope selects, so it only ever widens:
```yaml
golang:
capture-symbols: extended-stdlib
capture-symbols-include:
- github.com/klauspost/**
```
Patterns are standard doublestar globs, which matters because module paths carry `/v2`-style suffixes:
```yaml
golang:
capture-symbols-include:
- github.com/klauspost/* # compress, but not compress/v2
- github.com/klauspost/** # both
- k8s.io/client-go # exact match only
```
Ordering is `none` < `stdlib` < `extended-stdlib` < `all`. The existing three values
and the `none` default are unchanged, and the include list is inert under `none`.
Presets compile into glob lists internally, so a single matcher answers "does this
module get symbols" instead of a preset branch sitting next to a separate glob branch.
An unrecognized `capture-symbols` value still falls back to `none`, but warns now
instead of doing it silently. A malformed include pattern warns and gets skipped.
One thing worth a look beyond the feature: the `Symbols` field description in the JSON
schema was wrong after this (it claimed only `all` and `stdlib` populate anything), and
that description lives in the already-published `16.1.10`. Rather than bump a version for
a sentence, `16.1.10` is amended in place and `schema/json/README.md` grows an explicit
exception for description-only changes: descriptions only, no shape change of any kind,
`$id` unchanged. Anything else still needs a bump. Happy to split that into its own PR if
you'd rather review the policy separately.
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
* refactor(golang): rename capture-symbols-include to capture-symbols-modules
The key's entries are go module paths, and `-include` sitting next to `capture-symbols` reads as plausibly taking symbol or package names instead. Those spellings parse and match nothing, which is quieter than the confusion `-include` was picked to avoid, so the name now says what the list holds.
`golang.CatalogerConfig.CaptureSymbolsModules` and `WithCaptureSymbolsModules` rename with it. Nothing behavioral changes; the key is new in this PR so there is no compatibility surface.
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
* feat(golang): match capture-symbols-modules across major version suffixes
`github.com/anchore/*` covered `github.com/anchore/syft` and silently stopped covering it the day it became `github.com/anchore/syft/v2`. The config keeps parsing, nothing warns, and symbols quietly go missing from the SBOM. Exact paths had the same hole: `github.com/klauspost/compress` did not cover `compress/v2` either, so no spelling short of `**` survived a major bump.
A major version suffix is part of a module's path but not part of its identity, so patterns are now matched against the module path both with and without it, using `module.SplitPathVersion` from `golang.org/x/mod` (already a direct dep, already used in this package for `PseudoVersion`).
```yaml
golang:
capture-symbols-modules:
- github.com/klauspost/* # compress and compress/v2
- github.com/klauspost/compress # same module at every major version
- github.com/klauspost/compress/v2 # v2 alone
```
Only a trailing suffix is a version, which is Go's own rule. In `github.com/anchore/syft/v2/thing` the `v2` is an ordinary path element naming a major subdirectory a nested module lives in, so it stays literal and `github.com/anchore/**/thing` is how you reach it. `/v0` and `/v1` are not valid suffixes and are left alone.
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
---------
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
96 lines
2.7 KiB
Go
96 lines
2.7 KiB
Go
package options
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/anchore/syft/syft/cataloging"
|
|
)
|
|
|
|
func Test_golangConfig_PostLoad(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
cfg golangConfig
|
|
expected cataloging.SymbolScope
|
|
expectedModules []string
|
|
wantErr assert.ErrorAssertionFunc
|
|
}{
|
|
{
|
|
name: "normalize all",
|
|
cfg: golangConfig{CaptureSymbols: "all"},
|
|
expected: cataloging.SymbolScopeAll,
|
|
},
|
|
{
|
|
name: "normalize stdlib",
|
|
cfg: golangConfig{CaptureSymbols: "stdlib"},
|
|
expected: cataloging.SymbolScopeStdlib,
|
|
},
|
|
{
|
|
name: "normalize extended-stdlib",
|
|
cfg: golangConfig{CaptureSymbols: " Extended-Stdlib "},
|
|
expected: cataloging.SymbolScopeExtendedStdlib,
|
|
},
|
|
{
|
|
name: "module patterns keep embedded commas",
|
|
cfg: golangConfig{
|
|
CaptureSymbols: "stdlib",
|
|
// brace alternation contains a comma; splitting on it would corrupt the pattern
|
|
CaptureSymbolsModules: []string{"github.com/{foo,bar}/**", "golang.org/x/**"},
|
|
},
|
|
expected: cataloging.SymbolScopeStdlib,
|
|
expectedModules: []string{"github.com/{foo,bar}/**", "golang.org/x/**"},
|
|
},
|
|
{
|
|
// viper splits a comma-separated scalar (env var or bare yaml string) but does not trim,
|
|
// so a leading space would otherwise survive into a pattern that silently matches nothing
|
|
name: "module patterns are trimmed",
|
|
cfg: golangConfig{
|
|
CaptureSymbols: "stdlib",
|
|
CaptureSymbolsModules: []string{"golang.org/x/**", " github.com/foo/** "},
|
|
},
|
|
expected: cataloging.SymbolScopeStdlib,
|
|
expectedModules: []string{"golang.org/x/**", "github.com/foo/**"},
|
|
},
|
|
{
|
|
name: "empty defaults to none",
|
|
cfg: golangConfig{CaptureSymbols: ""},
|
|
expected: cataloging.SymbolScopeNone,
|
|
},
|
|
{
|
|
name: "invalid value defaults to none",
|
|
cfg: golangConfig{CaptureSymbols: "stdlbi"},
|
|
expected: cataloging.SymbolScopeNone,
|
|
},
|
|
{
|
|
name: "boolean spellings default to none",
|
|
cfg: golangConfig{CaptureSymbols: "true"},
|
|
expected: cataloging.SymbolScopeNone,
|
|
},
|
|
{
|
|
name: "explicit none resolves to none",
|
|
cfg: golangConfig{CaptureSymbols: "none"},
|
|
expected: cataloging.SymbolScopeNone,
|
|
},
|
|
{
|
|
name: "explicit none is not case sensitive",
|
|
cfg: golangConfig{CaptureSymbols: " NONE "},
|
|
expected: cataloging.SymbolScopeNone,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.wantErr == nil {
|
|
tt.wantErr = assert.NoError
|
|
}
|
|
err := tt.cfg.PostLoad()
|
|
tt.wantErr(t, err)
|
|
if err != nil {
|
|
return
|
|
}
|
|
assert.Equal(t, tt.expected, tt.cfg.CaptureSymbols)
|
|
assert.Equal(t, tt.expectedModules, tt.cfg.CaptureSymbolsModules)
|
|
})
|
|
}
|
|
}
|