From ef70d2076689605b9d7a0c956c1d8ff589fea02f Mon Sep 17 00:00:00 2001 From: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> Date: Wed, 23 Aug 2023 17:23:50 +0600 Subject: [PATCH] feat: add Package.resolved swift files support (#4932) * add Package.resolved files analyzer * add Swift detector and integration test * refactor after go-dep-parser changes * bump go-dep-parser * remove replaces * use filePath for Required func * add ID field --- .github/workflows/semantic-pr.yaml | 1 + go.mod | 4 +- go.sum | 8 +- integration/repo_test.go | 9 ++ integration/testdata/fixtures/db/swift.yaml | 14 +++ .../testdata/fixtures/db/vulnerability.yaml | 18 ++++ .../fixtures/repo/swift/Package.resolved | 25 ++++++ integration/testdata/swift.json.golden | 79 +++++++++++++++++ pkg/detector/library/driver.go | 5 ++ pkg/fanal/analyzer/all/import.go | 1 + pkg/fanal/analyzer/const.go | 1 + .../analyzer/language/swift/swift/swift.go | 46 ++++++++++ .../language/swift/swift/swift_test.go | 88 +++++++++++++++++++ .../swift/testdata/empty/Package.resolved | 1 + .../swift/testdata/happy/Package.resolved | 34 +++++++ pkg/fanal/types/const.go | 2 + 16 files changed, 330 insertions(+), 6 deletions(-) create mode 100644 integration/testdata/fixtures/db/swift.yaml create mode 100644 integration/testdata/fixtures/repo/swift/Package.resolved create mode 100644 integration/testdata/swift.json.golden create mode 100644 pkg/fanal/analyzer/language/swift/swift/swift.go create mode 100644 pkg/fanal/analyzer/language/swift/swift/swift_test.go create mode 100644 pkg/fanal/analyzer/language/swift/swift/testdata/empty/Package.resolved create mode 100644 pkg/fanal/analyzer/language/swift/swift/testdata/happy/Package.resolved diff --git a/.github/workflows/semantic-pr.yaml b/.github/workflows/semantic-pr.yaml index 02b7272bc24..04175b2a7e0 100644 --- a/.github/workflows/semantic-pr.yaml +++ b/.github/workflows/semantic-pr.yaml @@ -73,6 +73,7 @@ jobs: c\+\+ elixir dart + swift os lang diff --git a/go.mod b/go.mod index f1f56259c84..d9d8f493ae0 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/alicebob/miniredis/v2 v2.30.4 github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 github.com/aquasecurity/defsec v0.91.1 - github.com/aquasecurity/go-dep-parser v0.0.0-20230816082938-c86bfd152132 + github.com/aquasecurity/go-dep-parser v0.0.0-20230823094455-40c1f85cc942 github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798 github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 @@ -23,7 +23,7 @@ require ( github.com/aquasecurity/table v1.8.0 github.com/aquasecurity/testdocker v0.0.0-20230111101738-e741bda259da github.com/aquasecurity/tml v0.6.1 - github.com/aquasecurity/trivy-db v0.0.0-20230817085355-15ce04b6527c + github.com/aquasecurity/trivy-db v0.0.0-20230823084507-315928e846ff github.com/aquasecurity/trivy-java-db v0.0.0-20230209231723-7cddb1406728 github.com/aquasecurity/trivy-kubernetes v0.5.7-0.20230814115812-7afa52705226 github.com/aws/aws-sdk-go v1.44.273 diff --git a/go.sum b/go.sum index c34ab676c41..eb4829bc3fd 100644 --- a/go.sum +++ b/go.sum @@ -325,8 +325,8 @@ github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 h1:2a30 github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986/go.mod h1:NT+jyeCzXk6vXR5MTkdn4z64TgGfE5HMLC8qfj5unl8= github.com/aquasecurity/defsec v0.91.1 h1:dBIPm6Tva9I+ZTQv+6t9wob3ZlMSu8NFqMJr4mgJC5A= github.com/aquasecurity/defsec v0.91.1/go.mod h1:l/srzxtuuyb6c6FlqUvMp3xw2ZbvuZ0l9972MNJM7V8= -github.com/aquasecurity/go-dep-parser v0.0.0-20230816082938-c86bfd152132 h1:SiiJwsijT2zgXJLGAPc5xXYH6QAnZjfsegm6vi2h/qo= -github.com/aquasecurity/go-dep-parser v0.0.0-20230816082938-c86bfd152132/go.mod h1:0+GvQF0gL4YEAAUPpNeLeGpFDxMvvIHLMd7vk9bpwko= +github.com/aquasecurity/go-dep-parser v0.0.0-20230823094455-40c1f85cc942 h1:VGfeUtZyya9Vsl8enDurZ7pb/NDp2aJlL2rx2g4pR6A= +github.com/aquasecurity/go-dep-parser v0.0.0-20230823094455-40c1f85cc942/go.mod h1:0+GvQF0gL4YEAAUPpNeLeGpFDxMvvIHLMd7vk9bpwko= github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce h1:QgBRgJvtEOBtUXilDb1MLi1p1MWoyFDXAu5DEUl5nwM= github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce/go.mod h1:HXgVzOPvXhVGLJs4ZKO817idqr/xhwsTcj17CLYY74s= github.com/aquasecurity/go-mock-aws v0.0.0-20230328195059-5bf52338aec3 h1:Vt9y1gZS5JGY3tsL9zc++Cg4ofX51CG7PaMyC5SXWPg= @@ -345,8 +345,8 @@ github.com/aquasecurity/testdocker v0.0.0-20230111101738-e741bda259da h1:pj/adfN github.com/aquasecurity/testdocker v0.0.0-20230111101738-e741bda259da/go.mod h1:852lbQLpK2nCwlR4ZLYIccxYCfoQao6q9Nl6tjz54v8= github.com/aquasecurity/tml v0.6.1 h1:y2ZlGSfrhnn7t4ZJ/0rotuH+v5Jgv6BDDO5jB6A9gwo= github.com/aquasecurity/tml v0.6.1/go.mod h1:OnYMWY5lvI9ejU7yH9LCberWaaTBW7hBFsITiIMY2yY= -github.com/aquasecurity/trivy-db v0.0.0-20230817085355-15ce04b6527c h1:vsWd8uL6C1MoeYhEaJFQU+Rcx93Qv1eFe6N1BO2LjUs= -github.com/aquasecurity/trivy-db v0.0.0-20230817085355-15ce04b6527c/go.mod h1:iJSGMMclPEhkYeyiN9i+gzjV9jhEv+XfPzfVgFhfvTE= +github.com/aquasecurity/trivy-db v0.0.0-20230823084507-315928e846ff h1:+MLnPm81Msu921N/lBrKd/NwwBrrzRoTgyMq0pIUhbs= +github.com/aquasecurity/trivy-db v0.0.0-20230823084507-315928e846ff/go.mod h1:iJSGMMclPEhkYeyiN9i+gzjV9jhEv+XfPzfVgFhfvTE= github.com/aquasecurity/trivy-java-db v0.0.0-20230209231723-7cddb1406728 h1:0eS+V7SXHgqoT99tV1mtMW6HL4HdoB9qGLMCb1fZp8A= github.com/aquasecurity/trivy-java-db v0.0.0-20230209231723-7cddb1406728/go.mod h1:Ldya37FLi0e/5Cjq2T5Bty7cFkzUDwTcPeQua+2M8i8= github.com/aquasecurity/trivy-kubernetes v0.5.7-0.20230814115812-7afa52705226 h1:fL4BpAfnLFruHqkomRDAB7Lv8yv3zuKdg71mZk9y61c= diff --git a/integration/repo_test.go b/integration/repo_test.go index b1116e4282f..79801bc3e0a 100644 --- a/integration/repo_test.go +++ b/integration/repo_test.go @@ -175,6 +175,15 @@ func TestRepository(t *testing.T) { }, golden: "testdata/dotnet.json.golden", }, + { + name: "swift", + args: args{ + scanner: types.VulnerabilityScanner, + listAllPkgs: true, + input: "testdata/fixtures/repo/swift", + }, + golden: "testdata/swift.json.golden", + }, { name: "cocoapods", args: args{ diff --git a/integration/testdata/fixtures/db/swift.yaml b/integration/testdata/fixtures/db/swift.yaml new file mode 100644 index 00000000000..f380da408a3 --- /dev/null +++ b/integration/testdata/fixtures/db/swift.yaml @@ -0,0 +1,14 @@ +- bucket: "swift::GitHub Security Advisory Swift" + pairs: + - bucket: github.com/apple/swift-nio + pairs: + - key: CVE-2022-3215 + value: + PatchedVersions: + - "2.29.1" + - "2.39.1" + - "2.42.0" + VulnerableVersions: + - "< 2.29.1" + - ">= 2.39.0, < 2.39.1" + - ">= 2.41.0, < 2.42.0" \ No newline at end of file diff --git a/integration/testdata/fixtures/db/vulnerability.yaml b/integration/testdata/fixtures/db/vulnerability.yaml index 741693b197f..3165663ebf2 100644 --- a/integration/testdata/fixtures/db/vulnerability.yaml +++ b/integration/testdata/fixtures/db/vulnerability.yaml @@ -1290,6 +1290,24 @@ - https://github.com/advisories/GHSA-4rgh-jx4f-qfcq PublishedDate: "2022-05-24T17:37:16Z" LastModifiedDate: "2022-10-06T20:26:08Z" + - key: CVE-2022-3215 + value: + Title: "SwiftNIO vulnerable to Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP Response Splitting')" + Description: "`NIOHTTP1` and projects using it for generating HTTP responses, including SwiftNIO, can be subject to a HTTP Response Injection attack..." + Severity: MEDIUM + VendorSeverity: + ghsa: 2 + CVSS: + ghsa: + V3Vector: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N" + V3Score: 5.3 + References: + - https://github.com/apple/swift-nio/security/advisories/GHSA-7fj7-39wj-c64f + - https://nvd.nist.gov/vuln/detail/CVE-2022-3215 + - https://github.com/apple/swift-nio/commit/a16e2f54a25b2af217044e5168997009a505930f + - https://github.com/advisories/GHSA-7fj7-39wj-c64f + PublishedDate: "2023-06-07T16:01:53Z" + LastModifiedDate: "2023-06-19T16:45:07Z" - key: CVE-2022-24775 value: Title: "Improper Input Validation in guzzlehttp/psr7" diff --git a/integration/testdata/fixtures/repo/swift/Package.resolved b/integration/testdata/fixtures/repo/swift/Package.resolved new file mode 100644 index 00000000000..f9ca89fb741 --- /dev/null +++ b/integration/testdata/fixtures/repo/swift/Package.resolved @@ -0,0 +1,25 @@ +{ + "object": { + "pins": [ + { + "package": "swift-atomics", + "repositoryURL": "https://github.com/apple/swift-atomics.git", + "state": { + "branch": null, + "revision": "6c89474e62719ddcc1e9614989fff2f68208fe10", + "version": "1.1.0" + } + }, + { + "package": "swift-nio", + "repositoryURL": "https://github.com/apple/swift-nio", + "state": { + "branch": null, + "revision": "ece5057615d1bee848341eceafdf04ca54d60177", + "version": "2.41.0" + } + } + ] + }, + "version": 1 +} diff --git a/integration/testdata/swift.json.golden b/integration/testdata/swift.json.golden new file mode 100644 index 00000000000..ba7fe8ab567 --- /dev/null +++ b/integration/testdata/swift.json.golden @@ -0,0 +1,79 @@ +{ + "SchemaVersion": 2, + "ArtifactName": "testdata/fixtures/repo/swift", + "ArtifactType": "repository", + "Metadata": { + "ImageConfig": { + "architecture": "", + "created": "0001-01-01T00:00:00Z", + "os": "", + "rootfs": { + "type": "", + "diff_ids": null + }, + "config": {} + } + }, + "Results": [ + { + "Target": "Package.resolved", + "Class": "lang-pkgs", + "Type": "swift", + "Packages": [ + { + "ID": "github.com/apple/swift-atomics@1.1.0", + "Name": "github.com/apple/swift-atomics", + "Version": "1.1.0", + "Layer": {}, + "Locations": [ + { + "StartLine": 4, + "EndLine": 12 + } + ] + }, + { + "ID": "github.com/apple/swift-nio@2.41.0", + "Name": "github.com/apple/swift-nio", + "Version": "2.41.0", + "Layer": {}, + "Locations": [ + { + "StartLine": 13, + "EndLine": 21 + } + ] + } + ], + "Vulnerabilities": [ + { + "VulnerabilityID": "CVE-2022-3215", + "PkgID": "github.com/apple/swift-nio@2.41.0", + "PkgName": "github.com/apple/swift-nio", + "InstalledVersion": "2.41.0", + "FixedVersion": "2.29.1, 2.39.1, 2.42.0", + "Status": "fixed", + "Layer": {}, + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2022-3215", + "Title": "SwiftNIO vulnerable to Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP Response Splitting')", + "Description": "`NIOHTTP1` and projects using it for generating HTTP responses, including SwiftNIO, can be subject to a HTTP Response Injection attack...", + "Severity": "MEDIUM", + "CVSS": { + "ghsa": { + "V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N", + "V3Score": 5.3 + } + }, + "References": [ + "https://github.com/apple/swift-nio/security/advisories/GHSA-7fj7-39wj-c64f", + "https://nvd.nist.gov/vuln/detail/CVE-2022-3215", + "https://github.com/apple/swift-nio/commit/a16e2f54a25b2af217044e5168997009a505930f", + "https://github.com/advisories/GHSA-7fj7-39wj-c64f" + ], + "PublishedDate": "2023-06-07T16:01:53Z", + "LastModifiedDate": "2023-06-19T16:45:07Z" + } + ] + } + ] +} diff --git a/pkg/detector/library/driver.go b/pkg/detector/library/driver.go index 1b0c57e40de..395738d76e1 100644 --- a/pkg/detector/library/driver.go +++ b/pkg/detector/library/driver.go @@ -60,6 +60,11 @@ func NewDriver(libType string) (Driver, bool) { // Only semver can be used for version ranges // https://docs.conan.io/en/latest/versioning/version_ranges.html comparer = compare.GenericComparer{} + case ftypes.Swift: + // Swift uses semver + // https://www.swift.org/package-manager/#importing-dependencies + ecosystem = vulnerability.Swift + comparer = compare.GenericComparer{} case ftypes.Cocoapods: log.Logger.Warn("CocoaPods is supported for SBOM, not for vulnerability scanning") return Driver{}, false diff --git a/pkg/fanal/analyzer/all/import.go b/pkg/fanal/analyzer/all/import.go index 16e24195da3..743ae833771 100644 --- a/pkg/fanal/analyzer/all/import.go +++ b/pkg/fanal/analyzer/all/import.go @@ -32,6 +32,7 @@ import ( _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/rust/binary" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/rust/cargo" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/swift/cocoapods" + _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/swift/swift" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/licensing" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/alpine" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/amazonlinux" diff --git a/pkg/fanal/analyzer/const.go b/pkg/fanal/analyzer/const.go index 0eac3c37bab..5f2be4551df 100644 --- a/pkg/fanal/analyzer/const.go +++ b/pkg/fanal/analyzer/const.go @@ -84,6 +84,7 @@ const ( TypeMixLock Type = "mix-lock" // Swift + TypeSwift Type = "swift" TypeCocoaPods Type = "cocoapods" // Dart diff --git a/pkg/fanal/analyzer/language/swift/swift/swift.go b/pkg/fanal/analyzer/language/swift/swift/swift.go new file mode 100644 index 00000000000..a61e832a8f7 --- /dev/null +++ b/pkg/fanal/analyzer/language/swift/swift/swift.go @@ -0,0 +1,46 @@ +package swift + +import ( + "context" + "os" + "path" + + "github.com/aquasecurity/go-dep-parser/pkg/swift/swift" + "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language" + "github.com/aquasecurity/trivy/pkg/fanal/types" + + "golang.org/x/xerrors" +) + +func init() { + analyzer.RegisterAnalyzer(&swiftLockAnalyzer{}) +} + +const ( + version = 1 +) + +// swiftLockAnalyzer analyzes Package.resolved files +type swiftLockAnalyzer struct{} + +func (a swiftLockAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) { + p := swift.NewParser() + res, err := language.Analyze(types.Swift, input.FilePath, input.Content, p) + if err != nil { + return nil, xerrors.Errorf("%s parse error: %w", input.FilePath, err) + } + return res, nil +} + +func (a swiftLockAnalyzer) Required(filePath string, _ os.FileInfo) bool { + return path.Base(filePath) == types.SwiftResolved +} + +func (a swiftLockAnalyzer) Type() analyzer.Type { + return analyzer.TypeSwift +} + +func (a swiftLockAnalyzer) Version() int { + return version +} diff --git a/pkg/fanal/analyzer/language/swift/swift/swift_test.go b/pkg/fanal/analyzer/language/swift/swift/swift_test.go new file mode 100644 index 00000000000..fe12320bd92 --- /dev/null +++ b/pkg/fanal/analyzer/language/swift/swift/swift_test.go @@ -0,0 +1,88 @@ +package swift + +import ( + "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "os" + "testing" +) + +func Test_swiftLockAnalyzer_Analyze(t *testing.T) { + tests := []struct { + name string + inputFile string + want *analyzer.AnalysisResult + }{ + { + name: "happy path", + inputFile: "testdata/happy/Package.resolved", + want: &analyzer.AnalysisResult{ + Applications: []types.Application{ + { + Type: types.Swift, + FilePath: "testdata/happy/Package.resolved", + Libraries: types.Packages{ + + { + ID: "github.com/Quick/Nimble@9.2.1", + Name: "github.com/Quick/Nimble", + Version: "9.2.1", + Locations: []types.Location{ + { + StartLine: 4, + EndLine: 12, + }, + }, + }, + { + ID: "github.com/Quick/Quick@7.0.0", + Name: "github.com/Quick/Quick", + Version: "7.0.0", + Locations: []types.Location{ + { + StartLine: 13, + EndLine: 21, + }, + }, + }, + { + ID: "github.com/ReactiveCocoa/ReactiveSwift@7.1.1", + Name: "github.com/ReactiveCocoa/ReactiveSwift", + Version: "7.1.1", + Locations: []types.Location{ + { + StartLine: 22, + EndLine: 30, + }, + }, + }, + }, + }, + }, + }, + }, + { + name: "empty file", + inputFile: "testdata/empty/Package.resolved", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f, err := os.Open(tt.inputFile) + require.NoError(t, err) + defer f.Close() + + a := swiftLockAnalyzer{} + got, err := a.Analyze(nil, analyzer.AnalysisInput{ + FilePath: tt.inputFile, + Content: f, + }) + + assert.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/pkg/fanal/analyzer/language/swift/swift/testdata/empty/Package.resolved b/pkg/fanal/analyzer/language/swift/swift/testdata/empty/Package.resolved new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/pkg/fanal/analyzer/language/swift/swift/testdata/empty/Package.resolved @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/pkg/fanal/analyzer/language/swift/swift/testdata/happy/Package.resolved b/pkg/fanal/analyzer/language/swift/swift/testdata/happy/Package.resolved new file mode 100644 index 00000000000..73b088823ab --- /dev/null +++ b/pkg/fanal/analyzer/language/swift/swift/testdata/happy/Package.resolved @@ -0,0 +1,34 @@ +{ + "object": { + "pins": [ + { + "package": "Nimble", + "repositoryURL": "https://github.com/Quick/Nimble.git", + "state": { + "branch": null, + "revision": "c93f16c25af5770f0d3e6af27c9634640946b068", + "version": "9.2.1" + } + }, + { + "package": "Quick", + "repositoryURL": "https://github.com/Quick/Quick.git", + "state": { + "branch": null, + "revision": "e206b8deba0d01fce70388a6d9dc66cba5603958", + "version": "7.0.0" + } + }, + { + "package": "ReactiveSwift", + "repositoryURL": "https://github.com/ReactiveCocoa/ReactiveSwift", + "state": { + "branch": null, + "revision": "40c465af19b993344e84355c00669ba2022ca3cd", + "version": "7.1.1" + } + } + ] + }, + "version": 1 +} \ No newline at end of file diff --git a/pkg/fanal/types/const.go b/pkg/fanal/types/const.go index aea49b4d0fb..4a8e9f49c89 100644 --- a/pkg/fanal/types/const.go +++ b/pkg/fanal/types/const.go @@ -31,6 +31,7 @@ const ( RustBinary = "rustbinary" Conan = "conan" Cocoapods = "cocoapods" + Swift = "swift" Pub = "pub" Hex = "hex" @@ -80,6 +81,7 @@ const ( ConanLock = "conan.lock" CocoaPodsLock = "Podfile.lock" + SwiftResolved = "Package.resolved" PubSpecLock = "pubspec.lock"