-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
fix: merge go.sum into go.mod for Go <1.17 projects #2479
Description
Summary
For Go projects using Go <1.17, `go.mod` does not include indirect dependencies. Trivy's gomod analyzer is designed to merge adjacent `go.sum` into `go.mod` results to capture these indirect deps. However, this merge has never worked in Vuls because `scanLibraries()` processes each lockfile individually — the fanal PostAnalyzer's filesystem walk cannot find `go.sum` when only `go.mod` is passed.
This is a pre-existing bug (present in master), not a regression from #2476.
Impact
- Go <1.17 projects: indirect dependencies from `go.sum` are missing from scan results, potentially causing missed vulnerability detections
- Go 1.17+: no impact (indirect deps are listed in `go.mod`)
Root cause
`scanLibraries()` calls `AnalyzeLibrary()` per file. The fanal gomod PostAnalyzer expects both `go.mod` and `go.sum` to be present in its virtual filesystem, but only the single file being analyzed is copied there.
Fix plan
After #2476 is merged, modify `scanLibraries()` in `scanner/base.go` (and similarly in `scanner/pseudo.go` and `scanner/windows.go`).
Step 1: Buffer go.sum in scanLibraries
In the file processing loop, when a `go.sum` is encountered, save its contents keyed by directory instead of passing to `AnalyzeLibrary`:
```go
goSumContents := map[string][]byte{} // dir -> contents
for _, path := range detectFiles {
// ... existing path resolution and file reading ...
if filepath.Base(abspath) == "go.sum" {
dir := filepath.Dir(abspath)
goSumContents[dir] = contents
continue
}
// ... existing AnalyzeLibrary call ...
}
```
Step 2: After loop, merge go.sum into go.mod results
```go
for i, scanner := range l.LibraryScanners {
if scanner.Type != ftypes.GoModule {
continue
}
if !lessThanGo117(scanner) {
continue
}
dir := filepath.Dir(scanner.LockfilePath)
sumContents, ok := goSumContents[dir]
if !ok {
continue
}
mergeGoSum(&l.LibraryScanners[i], sumContents)
}
```
Step 3: Implement lessThanGo117
Reference: `pkg/fanal/analyzer/language/golang/mod/mod.go` line 268-276
```go
func lessThanGo117(scanner models.LibraryScanner) bool {
// In Go 1.17+, go.mod includes "// indirect" comments which Trivy
// parses into Relationship=RelationshipIndirect. If none are found,
// the project is likely Go <1.17.
for _, lib := range scanner.Libs {
if lib.Indirect {
return false
}
}
return true
}
```
Note: `models.Library` currently does not have an `Indirect` field. This needs to be added, or the check needs to use the raw `ftypes.Package.Relationship` before conversion to `models.Library`. The simpler approach is to check at the `ftypes.Application` level before `convertLibWithScanner`.
Step 4: Implement mergeGoSum
Reference: `pkg/fanal/analyzer/language/golang/mod/mod.go` line 279-302
```go
func mergeGoSum(scanner *models.LibraryScanner, sumContents []byte) {
sumParser := sum.NewParser()
r := xio.NopCloser(bytes.NewReader(sumContents))
sumPkgs, _, err := sumParser.Parse(context.Background(), r)
if err != nil {
return // best effort
}
existing := map[string]bool{}
for _, lib := range scanner.Libs {
existing[lib.Name] = true
}
for _, pkg := range sumPkgs {
if existing[pkg.Name] {
continue
}
scanner.Libs = append(scanner.Libs, models.Library{
Name: pkg.Name,
Version: pkg.Version,
PURL: newPURL(ftypes.GoModule, types.Metadata{}, pkg),
})
}
}
```
Step 5: Handle file discovery order
go.sum may appear before or after go.mod in the `detectFiles` list. The approach above handles this by:
- Buffering ALL go.sum files first (they are skipped in the main loop)
- Processing go.mod normally through `AnalyzeLibrary`
- After the loop, merging buffered go.sum into matching go.mod results
This requires the dispatch to recognize go.sum (currently returns `parserNone`). Change `dispatch.go` to still return `parserNone` for go.sum, but the `scanLibraries` loop needs to check for go.sum BEFORE calling `detectParserType`.
Reference code locations
- Trivy `lessThanGo117`: `~/go/pkg/mod/github.com/aquasecurity/trivy@v0.69.2/pkg/fanal/analyzer/language/golang/mod/mod.go` line 268
- Trivy `mergeGoSum`: same file, line 279
- Trivy `sum.NewParser`: `~/go/pkg/mod/github.com/aquasecurity/trivy@v0.69.2/pkg/dependency/parser/golang/sum/parse.go`
- Vuls `scanLibraries`: `scanner/base.go` (after deps: remove fanal framework, call Trivy parsers directly #2476 merge)
- Vuls `dispatch.go`: go.sum case at line ~98
- Vuls `models/library.go`: `Library` struct (may need `Indirect` field)
Testing
- Create a Go 1.16 style go.mod fixture (no `// indirect` comments) + matching go.sum
- Verify: with fix, indirect deps from go.sum appear in scan results
- Verify: Go 1.17+ go.mod is unaffected (no merge needed)
- Add golden test fixtures for both cases
- Run `make compare-lockfile` to verify no regressions
Blocked by
- deps: remove fanal framework, call Trivy parsers directly #2476 (deps: remove fanal framework) — must merge first to avoid conflicts
Discovered during
PR #2476 review — confirmed via `make compare-lockfile` A/B comparison that master has the same behavior.