From 650bf9c5052a2ba99a8debdcee854c2295414fc5 Mon Sep 17 00:00:00 2001 From: Damian Ellwart Date: Thu, 4 Apr 2024 00:22:06 +0200 Subject: [PATCH] chore: refactor and apply review fixes --- docs/docs/coverage/language/c.md | 10 +-- pkg/dependency/parser/c/conan/parse.go | 74 +++++++++++---------- pkg/dependency/parser/c/conan/parse_test.go | 12 ++++ 3 files changed, 58 insertions(+), 38 deletions(-) diff --git a/docs/docs/coverage/language/c.md b/docs/docs/coverage/language/c.md index 6efe1e87e62b..a1d127d81bf8 100644 --- a/docs/docs/coverage/language/c.md +++ b/docs/docs/coverage/language/c.md @@ -1,6 +1,6 @@ # C/C++ -Trivy supports [Conan][conan] C/C++ Package Manager. +Trivy supports Conan C/C++ Package Manager ([v1][conanV1] and [v2][conanV2] with limitations). The following scanners are supported. @@ -12,12 +12,14 @@ The following table provides an outline of the features Trivy offers. | Package manager | File | Transitive dependencies | Dev dependencies | [Dependency graph][dependency-graph] | Position | | --------------- | -------------- | :---------------------: | :--------------: | :----------------------------------: | :------: | -| Conan | conan.lock[^1] | ✓ | Excluded | ✓ | ✓ | +| Conan | conan.lock[^1] | ✓ [^2] | Excluded | ✓ | ✓ | ## Conan In order to detect dependencies, Trivy searches for `conan.lock`[^1]. -[conan]: https://docs.conan.io/1/index.html +[conanV1]: https://docs.conan.io/1/index.html +[conanV2]: https://docs.conan.io/2/ [dependency-graph]: ../../configuration/reporting.md#show-origins-of-vulnerable-dependencies -[^1]: `conan.lock` is default name. To scan a custom filename use [file-patterns](../../configuration/skipping.md#file-patterns) \ No newline at end of file +[^1]: `conan.lock` is default name. To scan a custom filename use [file-patterns](../../configuration/skipping.md#file-patterns) +[^2]: For `conan.lock` in version 2, indirect dependencies are included in analysis but not flagged explicitly in dependency tree \ No newline at end of file diff --git a/pkg/dependency/parser/c/conan/parse.go b/pkg/dependency/parser/c/conan/parse.go index 7292f0cec9a5..88333567bb1d 100644 --- a/pkg/dependency/parser/c/conan/parse.go +++ b/pkg/dependency/parser/c/conan/parse.go @@ -15,8 +15,9 @@ import ( xio "github.com/aquasecurity/trivy/pkg/x/io" ) -type LockFileV1 struct { +type LockFile struct { GraphLock GraphLock `json:"graph_lock"` + Requires Requires `json:"requires"` } type GraphLock struct { @@ -30,17 +31,21 @@ type Node struct { EndLine int } -type LockFileV2 struct { - Requires []string `json:"requires"` +type Require struct { + Dependency string + StartLine int + EndLine int } +type Requires []Require + type Parser struct{} func NewParser() types.Parser { return &Parser{} } -func (p *Parser) parseRequirementsV1(lock LockFileV1) ([]types.Library, []types.Dependency, error) { +func (p *Parser) parseV1(lock LockFile) ([]types.Library, []types.Dependency, error) { var libs []types.Library var deps []types.Dependency var directDeps []string @@ -54,7 +59,7 @@ func (p *Parser) parseRequirementsV1(lock LockFileV1) ([]types.Library, []types. if node.Ref == "" { continue } - lib, err := parseRefV1(node) + lib, err := toLibrary(node.Ref, node.StartLine, node.EndLine) if err != nil { log.Logger.Debug(err) continue @@ -92,38 +97,40 @@ func (p *Parser) parseRequirementsV1(lock LockFileV1) ([]types.Library, []types. return libs, deps, nil } -func (p *Parser) parseRequirementsV2(lock LockFileV2) ([]types.Library, []types.Dependency, error) { +func (p *Parser) parseV2(lock LockFile) ([]types.Library, []types.Dependency, error) { var libs []types.Library for _, req := range lock.Requires { - lib, _ := parseRefV2(req) + lib, err := toLibrary(req.Dependency, req.StartLine, req.EndLine) + if err != nil { + log.Logger.Debug(err) + continue + } + libs = append(libs, lib) } return libs, []types.Dependency{}, nil } func (p *Parser) Parse(r xio.ReadSeekerAt) ([]types.Library, []types.Dependency, error) { - var lockV1 LockFileV1 - var lockV2 LockFileV2 + var lock LockFile + input, err := io.ReadAll(r) if err != nil { return nil, nil, xerrors.Errorf("failed to read conan lock file: %w", err) } - - // try to parse requirements as conan v1.x - if err := jfather.Unmarshal(input, &lockV1); err != nil { + if err := jfather.Unmarshal(input, &lock); err != nil { return nil, nil, xerrors.Errorf("failed to decode conan lock file: %w", err) } - if lockV1.GraphLock.Nodes != nil { + + // try to parse requirements as conan v1.x + if lock.GraphLock.Nodes != nil { log.Logger.Debug("Handling conan lockfile as v1.x") - return p.parseRequirementsV1(lockV1) + return p.parseV1(lock) } else { // try to parse requirements as conan v2.x log.Logger.Debug("Handling conan lockfile as v2.x") - if err := jfather.Unmarshal(input, &lockV2); err != nil { - return nil, nil, xerrors.Errorf("failed to decode conan lock file: %w", err) - } - return p.parseRequirementsV2(lockV2) + return p.parseV2(lock) } } @@ -141,8 +148,8 @@ func parsePackage(text string) (string, string, error) { return ss[0], ss[1], nil } -func parseRefV1(node Node) (types.Library, error) { - name, version, err := parsePackage(node.Ref) +func toLibrary(pkg string, startLine int, endLine int) (types.Library, error) { + name, version, err := parsePackage(pkg) if err != nil { return types.Library{}, err } @@ -152,25 +159,13 @@ func parseRefV1(node Node) (types.Library, error) { Version: version, Locations: []types.Location{ { - StartLine: node.StartLine, - EndLine: node.EndLine, + StartLine: startLine, + EndLine: endLine, }, }, }, nil } -func parseRefV2(req string) (types.Library, error) { - name, version, err := parsePackage(req) - if err != nil { - return types.Library{}, err - } - return types.Library{ - ID: dependency.ID(ftypes.Conan, name, version), - Name: name, - Version: version, - }, nil -} - // UnmarshalJSONWithMetadata needed to detect start and end lines of deps func (n *Node) UnmarshalJSONWithMetadata(node jfather.Node) error { if err := node.Decode(&n); err != nil { @@ -181,3 +176,14 @@ func (n *Node) UnmarshalJSONWithMetadata(node jfather.Node) error { n.EndLine = node.Range().End.Line return nil } + +func (r *Require) UnmarshalJSONWithMetadata(node jfather.Node) error { + var dep string + if err := node.Decode(&dep); err != nil { + return err + } + r.Dependency = dep + r.StartLine = node.Range().Start.Line + r.EndLine = node.Range().End.Line + return nil +} diff --git a/pkg/dependency/parser/c/conan/parse_test.go b/pkg/dependency/parser/c/conan/parse_test.go index 7a5af31c8ca6..d32268dd512c 100644 --- a/pkg/dependency/parser/c/conan/parse_test.go +++ b/pkg/dependency/parser/c/conan/parse_test.go @@ -113,11 +113,23 @@ func TestParse(t *testing.T) { ID: "matrix/1.3", Name: "matrix", Version: "1.3", + Locations: []types.Location{ + { + StartLine: 5, + EndLine: 5, + }, + }, }, { ID: "sound32/1.0", Name: "sound32", Version: "1.0", + Locations: []types.Location{ + { + StartLine: 4, + EndLine: 4, + }, + }, }, }, wantDeps: []types.Dependency{},