blob: 40a1b1fe4d98b9a8856bedc4c7e0871f2c7ce26f [file] [log] [blame]
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package audit
import (
"container/list"
"go/types"
"golang.org/x/tools/go/ssa"
)
// VulnerableImports returns a list of vulnerability findings for packages imported by `pkgs`
// given the vulnerability and platform info captured in `env`.
//
// Returns all findings reachable from `pkgs` while analyzing each package only once, prefering
// findings of shorter import traces. For instance, given import chains
// A -> B -> V
// A -> D -> B -> V
// D -> B -> V
// where A and D are top level packages and V is a vulnerable package, VulnerableImports can return either
// A -> B -> V
// or
// D -> B -> V
// as traces of importing a vulnerable package V.
func VulnerableImports(pkgs []*ssa.Package, env Env) []Finding {
pkgVulns := createPkgVulns(env.Vulns)
var findings []Finding
seen := make(map[string]bool)
queue := list.New()
for _, pkg := range pkgs {
queue.PushBack(&importChain{pkg: pkg.Pkg})
}
for queue.Len() > 0 {
front := queue.Front()
v := front.Value.(*importChain)
queue.Remove(front)
pkg := v.pkg
if pkg == nil {
continue
}
if seen[pkg.Path()] {
continue
}
seen[pkg.Path()] = true
for _, imp := range pkg.Imports() {
vulns := queryPkgVulns(imp.Path(), env, pkgVulns)
if len(vulns) > 0 {
findings = append(findings,
Finding{
Symbol: imp.Path(),
Type: ImportType,
Trace: v.trace(),
Vulns: serialize(vulns),
weight: len(v.trace())})
}
queue.PushBack(&importChain{pkg: imp, parent: v})
}
}
return findings
}
// importChain helps doing BFS over package imports while remembering import chains.
type importChain struct {
pkg *types.Package
parent *importChain
}
func (chain *importChain) trace() []TraceElem {
if chain == nil {
return nil
}
return append(chain.parent.trace(), TraceElem{Description: chain.pkg.Path()})
}