From ea0cf0379aff0348fde87356dab37947800fc1b6 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Sat, 28 Sep 2024 11:06:02 +0600 Subject: [PATCH 001/108] fix(misconf): escape all special sequences (#7558) Signed-off-by: nikpivkin --- pkg/iac/terraform/resource_block.go | 40 +++++++++++---- pkg/iac/terraform/resource_block_test.go | 62 ++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 pkg/iac/terraform/resource_block_test.go diff --git a/pkg/iac/terraform/resource_block.go b/pkg/iac/terraform/resource_block.go index 339843423112..abce0fcb040e 100644 --- a/pkg/iac/terraform/resource_block.go +++ b/pkg/iac/terraform/resource_block.go @@ -3,7 +3,6 @@ package terraform import ( "bytes" "fmt" - "regexp" "strings" "text/template" ) @@ -106,19 +105,14 @@ func renderPrimitive(val any) string { func parseStringPrimitive(input string) string { // we must escape templating // ref: https://developer.hashicorp.com/terraform/language/expressions/strings#escape-sequences-1 - r := regexp.MustCompile(`((\$|\%)\{.+\})`) - ff := r.ReplaceAllStringFunc(input, func(s string) string { - s = strings.Replace(s, "$", "$$", 1) - s = strings.Replace(s, "%", "%%", 1) - return s - }) - if strings.Contains(ff, "\n") { + input = escapeSpecialSequences(input) + if strings.Contains(input, "\n") { return fmt.Sprintf(`< 0 && rune(input[i-1]) == r { + continue + } + + sb.WriteRune(r) + } else { + sb.WriteRune(r) + } + } + return sb.String() +} diff --git a/pkg/iac/terraform/resource_block_test.go b/pkg/iac/terraform/resource_block_test.go new file mode 100644 index 000000000000..37fb1d32fdcb --- /dev/null +++ b/pkg/iac/terraform/resource_block_test.go @@ -0,0 +1,62 @@ +package terraform + +import ( + "testing" + + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hclsyntax" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_EscapeSpecialSequences(t *testing.T) { + tests := []struct { + name string + inp string + expected string + }{ + { + name: "without special sequences", + inp: `"hello world\\"`, + expected: `"hello world\\"`, + }, + { + name: "interpolation", + inp: `"Hello, ${var.name}!"`, + expected: `"Hello, $${var.name}!"`, + }, + { + name: "directive", + inp: `"Hello, %{ if true }foo%{ else }bar%{ endif }!"`, + expected: `"Hello, %%{ if true }foo%%{ else }bar%%{ endif }!"`, + }, + { + name: "interpolation already escaped", + inp: `"Hello, $${var.name}!"`, + expected: `"Hello, $${var.name}!"`, + }, + { + name: "start with special character", + inp: `${var.name}!"`, + expected: `$${var.name}!"`, + }, + { + name: "grok pattern", + inp: "# Grok Pattern Template\ngrok_pattern = \"%{TIMESTAMP_ISO8601:time} \\\\[%{NUMBER:pid}\\\\] %{GREEDYDATA:message}\"", + expected: "# Grok Pattern Template\ngrok_pattern = \"%%{TIMESTAMP_ISO8601:time} \\\\[%%{NUMBER:pid}\\\\] %%{GREEDYDATA:message}\"", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := escapeSpecialSequences(tt.inp) + assert.Equal(t, tt.expected, got) + + // We make sure that the characters are properly escaped + _, diag := hclsyntax.ParseTemplate([]byte(got), "", hcl.InitialPos) + if diag.HasErrors() { + require.NoError(t, diag) + } + }) + } +} From ef0a27d515ff80762bf1959d44a8bde017ae06ec Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Sat, 28 Sep 2024 12:31:53 +0600 Subject: [PATCH 002/108] feat(misconf): add ability to disable checks by ID (#7536) Signed-off-by: nikpivkin Co-authored-by: Simar --- pkg/iac/rego/load.go | 6 +++ pkg/iac/rego/options.go | 11 +++++ pkg/iac/rego/scanner.go | 15 ++++--- pkg/iac/rego/scanner_test.go | 84 ++++++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+), 6 deletions(-) diff --git a/pkg/iac/rego/load.go b/pkg/iac/rego/load.go index a1c29d163fae..8f1033ecfa2d 100644 --- a/pkg/iac/rego/load.go +++ b/pkg/iac/rego/load.go @@ -295,6 +295,12 @@ func (s *Scanner) filterModules(retriever *MetadataRetriever) error { continue } + if IsBuiltinNamespace(getModuleNamespace(module)) { + if _, disabled := s.disabledCheckIDs[meta.ID]; disabled { // ignore builtin disabled checks + continue + } + } + if len(meta.InputOptions.Selectors) == 0 { s.logger.Warn( "Module has no input selectors - it will be loaded for all inputs!", diff --git a/pkg/iac/rego/options.go b/pkg/iac/rego/options.go index d4b622a68c16..5cc60284abdb 100644 --- a/pkg/iac/rego/options.go +++ b/pkg/iac/rego/options.go @@ -106,3 +106,14 @@ func WithCustomSchemas(schemas map[string][]byte) options.ScannerOption { } } } + +// WithDisabledCheckIDs disables checks by their ID (ID field in metadata) +func WithDisabledCheckIDs(ids ...string) options.ScannerOption { + return func(s options.ConfigurableScanner) { + if ss, ok := s.(*Scanner); ok { + for _, id := range ids { + ss.disabledCheckIDs[id] = struct{}{} + } + } + } +} diff --git a/pkg/iac/rego/scanner.go b/pkg/iac/rego/scanner.go index a8e1caf525b3..9cf80fc83da0 100644 --- a/pkg/iac/rego/scanner.go +++ b/pkg/iac/rego/scanner.go @@ -69,6 +69,8 @@ type Scanner struct { embeddedLibs map[string]*ast.Module embeddedChecks map[string]*ast.Module customSchemas map[string][]byte + + disabledCheckIDs map[string]struct{} } func (s *Scanner) SetIncludeDeprecatedChecks(b bool) { @@ -109,12 +111,13 @@ func NewScanner(source types.Source, opts ...options.ScannerOption) *Scanner { } s := &Scanner{ - regoErrorLimit: ast.CompileErrorLimitDefault, - sourceType: source, - ruleNamespaces: make(map[string]struct{}), - runtimeValues: addRuntimeValues(), - logger: log.WithPrefix("rego"), - customSchemas: make(map[string][]byte), + regoErrorLimit: ast.CompileErrorLimitDefault, + sourceType: source, + ruleNamespaces: make(map[string]struct{}), + runtimeValues: addRuntimeValues(), + logger: log.WithPrefix("rego"), + customSchemas: make(map[string][]byte), + disabledCheckIDs: make(map[string]struct{}), } maps.Copy(s.ruleNamespaces, builtinNamespaces) diff --git a/pkg/iac/rego/scanner_test.go b/pkg/iac/rego/scanner_test.go index 73ef55223f14..1310e56d2ec4 100644 --- a/pkg/iac/rego/scanner_test.go +++ b/pkg/iac/rego/scanner_test.go @@ -1153,3 +1153,87 @@ deny { }) } } + +func Test_RegoScanner_WithDisabledCheckIDs(t *testing.T) { + + check := `# METADATA +# custom: +# id: TEST-001 +# avd_id: AVD-TEST-001 +# severity: LOW +# provider: aws +# service: s3 +# short_code: test +package builtin.test + +deny { + true +} +` + + tests := []struct { + name string + disabledChecks []string + inputCheck string + expected bool + }{ + { + name: "no disabled checks", + expected: true, + inputCheck: check, + }, + { + name: "disable check by ID", + disabledChecks: []string{"TEST-001"}, + inputCheck: check, + }, + { + name: "disabling a non-existent check", + disabledChecks: []string{"FOO"}, + expected: true, + inputCheck: check, + }, + { + name: "one of the identifiers does not exist", + disabledChecks: []string{"FOO", "TEST-001"}, + inputCheck: check, + }, + { + name: "do not disable user checks with builtin IDs", + inputCheck: `# METADATA +# custom: +# id: TEST-001 +# avd_id: AVD-TEST-001 +# severity: LOW +# provider: aws +# service: s3 +# short_code: test +package user.test + +deny { + true +} +`, + disabledChecks: []string{"TEST-001"}, + expected: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + scanner := rego.NewScanner( + types.SourceYAML, + rego.WithPolicyReader(strings.NewReader(tt.inputCheck)), + rego.WithDisabledCheckIDs(tt.disabledChecks...), + rego.WithPolicyNamespaces("user"), + ) + + require.NoError(t, scanner.LoadPolicies(nil)) + results, err := scanner.ScanInput(context.TODO(), rego.Input{}) + require.NoError(t, err) + + require.Equal(t, tt.expected, len(results.GetFailed()) > 0) + }) + } +} From efdb68d3b9ddf9dfaf45ea5855b31c43a4366bab Mon Sep 17 00:00:00 2001 From: Marcus Meissner Date: Sun, 29 Sep 2024 20:23:34 +0200 Subject: [PATCH 003/108] feat(suse): added SUSE Linux Enterprise Micro support (#7294) Signed-off-by: Marcus Meissner Signed-off-by: knqyf263 Co-authored-by: knqyf263 --- docs/docs/coverage/os/index.md | 3 +- docs/docs/coverage/os/suse.md | 3 +- integration/client_server_test.go | 7 + integration/docker_engine_test.go | 6 + integration/standalone_tar_test.go | 8 + integration/testdata/fixtures/db/suse.yaml | 19 ++ .../testdata/fixtures/db/vulnerability.yaml | 9 + .../testdata/opensuse-leap-151.json.golden | 4 +- .../testdata/opensuse-tumbleweed.json.golden | 2 +- .../testdata/sl-micro-rancher5.4.json.golden | 69 +++++ pkg/detector/ospkg/detect.go | 1 + pkg/detector/ospkg/suse/suse.go | 21 ++ pkg/detector/ospkg/suse/suse_test.go | 80 ++++++ .../suse/testdata/fixtures/data-source.yaml | 5 + .../ospkg/suse/testdata/fixtures/suse.yaml | 20 ++ pkg/fanal/analyzer/os/release/release.go | 5 + pkg/fanal/analyzer/os/release/release_test.go | 30 +++ .../analyzer/os/release/testdata/slemicro | 8 + .../os/release/testdata/slemicro-rancher | 13 + .../analyzer/os/release/testdata/slemicro6.0 | 10 + .../packages/opensuse-tumbleweed.json.golden | 252 +++++++++--------- pkg/fanal/types/const.go | 1 + pkg/purl/purl.go | 13 +- 23 files changed, 453 insertions(+), 136 deletions(-) create mode 100644 integration/testdata/fixtures/db/suse.yaml create mode 100644 integration/testdata/sl-micro-rancher5.4.json.golden create mode 100644 pkg/fanal/analyzer/os/release/testdata/slemicro create mode 100644 pkg/fanal/analyzer/os/release/testdata/slemicro-rancher create mode 100644 pkg/fanal/analyzer/os/release/testdata/slemicro6.0 diff --git a/docs/docs/coverage/os/index.md b/docs/docs/coverage/os/index.md index a28e113f07c9..a49793db8124 100644 --- a/docs/docs/coverage/os/index.md +++ b/docs/docs/coverage/os/index.md @@ -23,7 +23,8 @@ Trivy supports operating systems for | [Amazon Linux](amazon.md) | 1, 2, 2023 | dnf/yum/rpm | | [openSUSE Leap](suse.md) | 42, 15 | zypper/rpm | | [openSUSE Tumbleweed](suse.md) | (n/a) | zypper/rpm | -| [SUSE Enterprise Linux](suse.md) | 11, 12, 15 | zypper/rpm | +| [SUSE Linux Enterprise](suse.md) | 11, 12, 15 | zypper/rpm | +| [SUSE Linux Enterprise Micro](suse.md)| 5, 6 | zypper/rpm | | [Photon OS](photon.md) | 1.0, 2.0, 3.0, 4.0 | tndf/yum/rpm | | [Debian GNU/Linux](debian.md) | 7, 8, 9, 10, 11, 12 | apt/dpkg | | [Ubuntu](ubuntu.md) | All versions supported by Canonical | apt/dpkg | diff --git a/docs/docs/coverage/os/suse.md b/docs/docs/coverage/os/suse.md index 15cfb1e9379a..b28fed13425b 100644 --- a/docs/docs/coverage/os/suse.md +++ b/docs/docs/coverage/os/suse.md @@ -3,7 +3,8 @@ Trivy supports the following distributions: - openSUSE Leap - openSUSE Tumbleweed -- SUSE Enterprise Linux (SLE) +- SUSE Linux Enterprise (SLE) +- SUSE Linux Enterprise Micro Please see [here](index.md#supported-os) for supported versions. diff --git a/integration/client_server_test.go b/integration/client_server_test.go index adeab3f4c7b3..07a4f086200c 100644 --- a/integration/client_server_test.go +++ b/integration/client_server_test.go @@ -220,6 +220,13 @@ func TestClientServer(t *testing.T) { }, golden: "testdata/opensuse-tumbleweed.json.golden", }, + { + name: "sle micro rancher 5.4", + args: csArgs{ + Input: "testdata/fixtures/images/sle-micro-rancher-5.4_ndb.tar.gz", + }, + golden: "testdata/sl-micro-rancher5.4.json.golden", + }, { name: "photon 3.0", args: csArgs{ diff --git a/integration/docker_engine_test.go b/integration/docker_engine_test.go index 5b62d391eead..7f9a3119a156 100644 --- a/integration/docker_engine_test.go +++ b/integration/docker_engine_test.go @@ -198,6 +198,12 @@ func TestDockerEngine(t *testing.T) { input: "testdata/fixtures/images/opensuse-tumbleweed.tar.gz", golden: "testdata/opensuse-tumbleweed.json.golden", }, + { + name: "sle micro rancher 5.4", + imageTag: "ghcr.io/aquasecurity/trivy-test-images:sle-micro-rancher-5.4_ndb", + input: "testdata/fixtures/images/sle-micro-rancher-5.4_ndb.tar.gz", + golden: "testdata/sl-micro-rancher5.4.json.golden", + }, { name: "photon 3.0", imageTag: "ghcr.io/aquasecurity/trivy-test-images:photon-30", diff --git a/integration/standalone_tar_test.go b/integration/standalone_tar_test.go index dce852cf7f3c..039293cbfd56 100644 --- a/integration/standalone_tar_test.go +++ b/integration/standalone_tar_test.go @@ -341,6 +341,14 @@ func TestTar(t *testing.T) { }, golden: "testdata/opensuse-tumbleweed.json.golden", }, + { + name: "sle micro rancher 5.4", + args: args{ + Format: types.FormatJSON, + Input: "testdata/fixtures/images/sle-micro-rancher-5.4_ndb.tar.gz", + }, + golden: "testdata/sl-micro-rancher5.4.json.golden", + }, { name: "photon 3.0", args: args{ diff --git a/integration/testdata/fixtures/db/suse.yaml b/integration/testdata/fixtures/db/suse.yaml new file mode 100644 index 000000000000..690360c5de13 --- /dev/null +++ b/integration/testdata/fixtures/db/suse.yaml @@ -0,0 +1,19 @@ +- bucket: "SUSE Linux Enterprise 15-SP3" + pairs: + - bucket: libopenssl1_1 + pairs: + - key: "SUSE-SU-2022:2251-1" + value: + FixedVersion: 1.1.1d-150200.11.48.1 + - bucket: openssl-1_1 + pairs: + - key: "SUSE-SU-2022:2251-1" + value: + FixedVersion: 1.1.1d-150200.11.48.1 +- bucket: "SUSE Linux Enterprise Micro 5.3" + pairs: + - bucket: libopenssl1_1 + pairs: + - key: "SUSE-SU-2023:0311-1" + value: + FixedVersion: 1.1.1l-150400.7.22.1 diff --git a/integration/testdata/fixtures/db/vulnerability.yaml b/integration/testdata/fixtures/db/vulnerability.yaml index 6fcdcece75bd..e18d731a30c8 100644 --- a/integration/testdata/fixtures/db/vulnerability.yaml +++ b/integration/testdata/fixtures/db/vulnerability.yaml @@ -1349,6 +1349,15 @@ - "https://www.suse.com/security/cve/CVE-2023-2975/" - "https://www.suse.com/security/cve/CVE-2023-3446/" - "https://www.suse.com/support/security/rating/" + - key: SUSE-SU-2022:2251-1 + value: + Title: "Security update for openssl-1_1" + Description: "This update for openssl-1_1 fixes the following issues:\nCVE-2022-1292: Fixed command injection in c_rehash (bsc#1199166).\nCVE-2022-2068: Fixed more shell code injection issues in c_rehash. (bsc#1200550)" + Severity: MEDIUM + References: + - "https://www.suse.com/security/cve/CVE-2022-1292/" + - "https://www.suse.com/security/cve/CVE-2022-2068/" + - "https://www.suse.com/support/security/rating/" - key: CVE-2022-22965 value: Title: "spring-framework: RCE via Data Binding on JDK 9+" diff --git a/integration/testdata/opensuse-leap-151.json.golden b/integration/testdata/opensuse-leap-151.json.golden index 77b0148604bb..dadc7adcf600 100644 --- a/integration/testdata/opensuse-leap-151.json.golden +++ b/integration/testdata/opensuse-leap-151.json.golden @@ -66,7 +66,7 @@ "PkgID": "libopenssl1_1@1.1.0i-lp151.8.3.1.x86_64", "PkgName": "libopenssl1_1", "PkgIdentifier": { - "PURL": "pkg:rpm/opensuse.leap/libopenssl1_1@1.1.0i-lp151.8.3.1?arch=x86_64\u0026distro=opensuse.leap-15.1", + "PURL": "pkg:rpm/opensuse/libopenssl1_1@1.1.0i-lp151.8.3.1?arch=x86_64\u0026distro=opensuse.leap-15.1", "UID": "898b73ddd0412f57" }, "InstalledVersion": "1.1.0i-lp151.8.3.1", @@ -99,7 +99,7 @@ "PkgID": "openssl-1_1@1.1.0i-lp151.8.3.1.x86_64", "PkgName": "openssl-1_1", "PkgIdentifier": { - "PURL": "pkg:rpm/opensuse.leap/openssl-1_1@1.1.0i-lp151.8.3.1?arch=x86_64\u0026distro=opensuse.leap-15.1", + "PURL": "pkg:rpm/opensuse/openssl-1_1@1.1.0i-lp151.8.3.1?arch=x86_64\u0026distro=opensuse.leap-15.1", "UID": "58980d005de43f54" }, "InstalledVersion": "1.1.0i-lp151.8.3.1", diff --git a/integration/testdata/opensuse-tumbleweed.json.golden b/integration/testdata/opensuse-tumbleweed.json.golden index b3dc552bdfca..a15146616bc2 100644 --- a/integration/testdata/opensuse-tumbleweed.json.golden +++ b/integration/testdata/opensuse-tumbleweed.json.golden @@ -69,7 +69,7 @@ "PkgID": "libopenssl3@3.1.4-9.1.x86_64", "PkgName": "libopenssl3", "PkgIdentifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libopenssl3@3.1.4-9.1?arch=x86_64\u0026distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libopenssl3@3.1.4-9.1?arch=x86_64\u0026distro=opensuse.tumbleweed-20240607", "UID": "f051425f385d2b99" }, "InstalledVersion": "3.1.4-9.1", diff --git a/integration/testdata/sl-micro-rancher5.4.json.golden b/integration/testdata/sl-micro-rancher5.4.json.golden new file mode 100644 index 000000000000..99e2ad4ca599 --- /dev/null +++ b/integration/testdata/sl-micro-rancher5.4.json.golden @@ -0,0 +1,69 @@ +{ + "SchemaVersion": 2, + "CreatedAt": "2021-08-25T12:20:30.000000005Z", + "ArtifactName": "testdata/fixtures/images/sle-micro-rancher-5.4_ndb.tar.gz", + "ArtifactType": "container_image", + "Metadata": { + "OS": { + "Family": "suse linux enterprise micro", + "Name": "5.4" + }, + "ImageID": "sha256:c45ec974938acac29c893b5d273d73e4ebdd7e6a97b6fa861dfbd8dd430b9016", + "DiffIDs": [ + "sha256:7cdd3aec849d122d63dc83a5e1e2fb89b341c67b03e25979131ca335a463bb57" + ], + "ImageConfig": { + "architecture": "amd64", + "author": "SUSE LLC (https://www.suse.com/)", + "created": "2024-09-03T17:54:39Z", + "history": [ + { + "author": "SUSE LLC \u003chttps://www.suse.com/\u003e", + "created": "2024-09-03T17:54:39Z", + "created_by": "KIWI 9.24.43" + } + ], + "os": "linux", + "rootfs": { + "type": "layers", + "diff_ids": [ + "sha256:7cdd3aec849d122d63dc83a5e1e2fb89b341c67b03e25979131ca335a463bb57" + ] + }, + "config": { + "Cmd": [ + "/bin/bash" + ], + "Labels": { + "com.suse.eula": "sle-eula", + "com.suse.image-type": "sle-micro", + "com.suse.release-stage": "released", + "com.suse.sle.micro.rancher.created": "2024-09-03T17:53:32.129328086Z", + "com.suse.sle.micro.rancher.description": "Image containing a micro environment for containers based on the SLE Micro for Rancher.", + "com.suse.sle.micro.rancher.disturl": "obs://build.suse.de/SUSE:SLE-15-SP4:Update:Products:Micro54:Update:CR/images/fcaa3a91b132f1955fa900b902aef7f2-SLE-Micro-Rancher", + "com.suse.sle.micro.rancher.reference": "registry.suse.com/suse/sle-micro-rancher/5.4:%PKG_VERSION%-%RELEASE", + "com.suse.sle.micro.rancher.title": "SLE Micro for Rancher Base Container", + "com.suse.sle.micro.rancher.url": "https://www.suse.com/products/micro/", + "com.suse.sle.micro.rancher.vendor": "SUSE LLC", + "com.suse.sle.micro.rancher.version": "5.4", + "com.suse.supportlevel": "l3", + "org.openbuildservice.disturl": "obs://build.suse.de/SUSE:SLE-15-SP4:Update:Products:Micro54:Update:CR/images/fcaa3a91b132f1955fa900b902aef7f2-SLE-Micro-Rancher", + "org.opencontainers.image.created": "2024-09-03T17:53:32.129328086Z", + "org.opencontainers.image.description": "Image containing a micro environment for containers based on the SLE Micro for Rancher.", + "org.opencontainers.image.title": "SLE Micro for Rancher Base Container", + "org.opencontainers.image.url": "https://www.suse.com/products/micro/", + "org.opencontainers.image.vendor": "SUSE LLC", + "org.opencontainers.image.version": "5.4", + "org.suse.reference": "registry.suse.com/suse/sle-micro-rancher/5.4:%PKG_VERSION%-%RELEASE" + } + } + } + }, + "Results": [ + { + "Target": "testdata/fixtures/images/sle-micro-rancher-5.4_ndb.tar.gz (suse linux enterprise micro 5.4)", + "Class": "os-pkgs", + "Type": "suse linux enterprise micro" + } + ] +} diff --git a/pkg/detector/ospkg/detect.go b/pkg/detector/ospkg/detect.go index 0f4a1df2a9d3..fedc8d31c9c2 100644 --- a/pkg/detector/ospkg/detect.go +++ b/pkg/detector/ospkg/detect.go @@ -44,6 +44,7 @@ var ( ftypes.OpenSUSETumbleweed: suse.NewScanner(suse.OpenSUSETumbleweed), ftypes.OpenSUSELeap: suse.NewScanner(suse.OpenSUSE), ftypes.SLES: suse.NewScanner(suse.SUSEEnterpriseLinux), + ftypes.SLEMicro: suse.NewScanner(suse.SUSEEnterpriseLinuxMicro), ftypes.Photon: photon.NewScanner(), ftypes.Wolfi: wolfi.NewScanner(), ftypes.Chainguard: chainguard.NewScanner(), diff --git a/pkg/detector/ospkg/suse/suse.go b/pkg/detector/ospkg/suse/suse.go index b999e1dafb22..36a26b335a96 100644 --- a/pkg/detector/ospkg/suse/suse.go +++ b/pkg/detector/ospkg/suse/suse.go @@ -44,6 +44,18 @@ var ( // 6 months after SLES 15 SP7 release // "15.7": time.Date(2031, 7, 31, 23, 59, 59, 0, time.UTC), } + slemicroEolDates = map[string]time.Time{ + // Source: https://www.suse.com/lifecycle/ + "5.0": time.Date(2022, 3, 31, 23, 59, 59, 0, time.UTC), + "5.1": time.Date(2025, 10, 31, 23, 59, 59, 0, time.UTC), + "5.2": time.Date(2026, 4, 30, 23, 59, 59, 0, time.UTC), + "5.3": time.Date(2026, 10, 30, 23, 59, 59, 0, time.UTC), + "5.4": time.Date(2027, 4, 30, 23, 59, 59, 0, time.UTC), + "5.5": time.Date(2027, 10, 31, 23, 59, 59, 0, time.UTC), + "6.0": time.Date(2028, 6, 30, 23, 59, 59, 0, time.UTC), + // 6.1 will be released late 2024 + // "6.1": time.Date(2028, 11, 30, 23, 59, 59, 0, time.UTC), + } opensuseEolDates = map[string]time.Time{ // Source: https://en.opensuse.org/Lifetime @@ -66,6 +78,8 @@ type Type int const ( // SUSEEnterpriseLinux is Linux Enterprise version SUSEEnterpriseLinux Type = iota + // SUSE Linux Enterprise Micro is the micro series + SUSEEnterpriseLinuxMicro // OpenSUSE for open versions OpenSUSE OpenSUSETumbleweed @@ -83,6 +97,10 @@ func NewScanner(t Type) *Scanner { return &Scanner{ vs: susecvrf.NewVulnSrc(susecvrf.SUSEEnterpriseLinux), } + case SUSEEnterpriseLinuxMicro: + return &Scanner{ + vs: susecvrf.NewVulnSrc(susecvrf.SUSEEnterpriseLinuxMicro), + } case OpenSUSE: return &Scanner{ vs: susecvrf.NewVulnSrc(susecvrf.OpenSUSE), @@ -135,6 +153,9 @@ func (s *Scanner) IsSupportedVersion(ctx context.Context, osFamily ftypes.OSType if osFamily == ftypes.SLES { return osver.Supported(ctx, slesEolDates, osFamily, osVer) } + if osFamily == ftypes.SLEMicro { + return osver.Supported(ctx, slemicroEolDates, osFamily, osVer) + } // tumbleweed is a rolling release, it has no version and no eol if osFamily == ftypes.OpenSUSETumbleweed { return true diff --git a/pkg/detector/ospkg/suse/suse_test.go b/pkg/detector/ospkg/suse/suse_test.go index 4db052743e82..9d480f18b0ea 100644 --- a/pkg/detector/ospkg/suse/suse_test.go +++ b/pkg/detector/ospkg/suse/suse_test.go @@ -111,6 +111,86 @@ func TestScanner_Detect(t *testing.T) { }, }, }, + { + name: "happy path: suse sle 15sp3", + fixtures: []string{ + "testdata/fixtures/suse.yaml", + "testdata/fixtures/data-source.yaml", + }, + distribution: suse.SUSEEnterpriseLinux, + args: args{ + osVer: "15.3", + pkgs: []ftypes.Package{ + { + Name: "libopenssl1_1", + Version: "1.1.1d", + Release: "150200.11.47.1", + SrcName: "libopenssl1_1", + SrcVersion: "1.1.1d", + SrcRelease: "150200.11.47.1", + Layer: ftypes.Layer{ + DiffID: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02", + }, + }, + }, + }, + want: []types.DetectedVulnerability{ + { + PkgName: "libopenssl1_1", + VulnerabilityID: "SUSE-SU-2022:2251-1", + InstalledVersion: "1.1.1d-150200.11.47.1", + FixedVersion: "1.1.1d-150200.11.48.1", + Layer: ftypes.Layer{ + DiffID: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02", + }, + DataSource: &dbTypes.DataSource{ + ID: vulnerability.SuseCVRF, + Name: "SUSE CVRF", + URL: "https://ftp.suse.com/pub/projects/security/cvrf/", + }, + }, + }, + }, + { + name: "happy path: suse sle micro 15.3", + fixtures: []string{ + "testdata/fixtures/suse.yaml", + "testdata/fixtures/data-source.yaml", + }, + distribution: suse.SUSEEnterpriseLinuxMicro, + args: args{ + osVer: "5.3", + pkgs: []ftypes.Package{ + { + Name: "libopenssl1_1", + Version: "1.1.1l", + Release: "150400.7.21.1", + SrcName: "libopenssl1_1", + SrcVersion: "1.1.1l", + SrcRelease: "150400.7.21.1", + Layer: ftypes.Layer{ + DiffID: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02", + }, + }, + }, + }, + want: []types.DetectedVulnerability{ + { + PkgName: "libopenssl1_1", + VulnerabilityID: "SUSE-SU-2023:0311-1", + InstalledVersion: "1.1.1l-150400.7.21.1", + FixedVersion: "1.1.1l-150400.7.22.1", + Layer: ftypes.Layer{ + DiffID: "sha256:932da51564135c98a49a34a193d6cd363d8fa4184d957fde16c9d8527b3f3b02", + }, + DataSource: &dbTypes.DataSource{ + ID: vulnerability.SuseCVRF, + Name: "SUSE CVRF", + URL: "https://ftp.suse.com/pub/projects/security/cvrf/", + }, + }, + }, + }, { name: "broken bucket", fixtures: []string{ diff --git a/pkg/detector/ospkg/suse/testdata/fixtures/data-source.yaml b/pkg/detector/ospkg/suse/testdata/fixtures/data-source.yaml index b917b7e19da1..4e9ddde802d5 100644 --- a/pkg/detector/ospkg/suse/testdata/fixtures/data-source.yaml +++ b/pkg/detector/ospkg/suse/testdata/fixtures/data-source.yaml @@ -15,3 +15,8 @@ ID: "suse-cvrf" Name: "SUSE CVRF" URL: "https://ftp.suse.com/pub/projects/security/cvrf/" + - key: SUSE Linux Enterprise Micro 5.3 + value: + ID: "suse-cvrf" + Name: "SUSE CVRF" + URL: "https://ftp.suse.com/pub/projects/security/cvrf/" diff --git a/pkg/detector/ospkg/suse/testdata/fixtures/suse.yaml b/pkg/detector/ospkg/suse/testdata/fixtures/suse.yaml index 6a17594af66c..027e56673d0e 100644 --- a/pkg/detector/ospkg/suse/testdata/fixtures/suse.yaml +++ b/pkg/detector/ospkg/suse/testdata/fixtures/suse.yaml @@ -8,3 +8,23 @@ - key: CVE-2021-0001 value: FixedVersion: "" +- bucket: SUSE Linux Enterprise 15.3 + pairs: + - bucket: libopenssl1_1 + pairs: + - key: "SUSE-SU-2022:2251-1" + value: + FixedVersion: 1.1.1d-150200.11.48.1 + - bucket: openssl-1_1 + pairs: + - key: "SUSE-SU-2022:2251-1" + value: + FixedVersion: 1.1.1d-150200.11.48.1 +- bucket: SUSE Linux Enterprise Micro 5.3 + pairs: + - bucket: libopenssl1_1 + pairs: + - key: "SUSE-SU-2023:0311-1" + value: + FixedVersion: 1.1.1l-150400.7.22.1 + diff --git a/pkg/fanal/analyzer/os/release/release.go b/pkg/fanal/analyzer/os/release/release.go index 8da24644d5f7..67a18714ac1a 100644 --- a/pkg/fanal/analyzer/os/release/release.go +++ b/pkg/fanal/analyzer/os/release/release.go @@ -55,6 +55,11 @@ func (a osReleaseAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInp family = types.OpenSUSELeap case "sles": family = types.SLES + // There are various rebrands of SLE Micro, there is also one brief (and reverted rebrand) + // for SLE Micro 6.0. which was called "SL Micro 6.0" until very short before release + // and there is a "SLE Micro for Rancher" rebrand, which is used by SUSEs K8S based offerings. + case "sle-micro", "sl-micro", "sle-micro-rancher": + family = types.SLEMicro case "photon": family = types.Photon case "wolfi": diff --git a/pkg/fanal/analyzer/os/release/release_test.go b/pkg/fanal/analyzer/os/release/release_test.go index 3b534ad7b14d..532e01d9efef 100644 --- a/pkg/fanal/analyzer/os/release/release_test.go +++ b/pkg/fanal/analyzer/os/release/release_test.go @@ -70,6 +70,36 @@ func Test_osReleaseAnalyzer_Analyze(t *testing.T) { }, }, }, + { + name: "SUSE Linux Enterprise Micro", + inputFile: "testdata/slemicro", + want: &analyzer.AnalysisResult{ + OS: types.OS{ + Family: types.SLEMicro, + Name: "5.3", + }, + }, + }, + { + name: "SUSE Linux Enterprise Micro 6.0", + inputFile: "testdata/slemicro6.0", + want: &analyzer.AnalysisResult{ + OS: types.OS{ + Family: types.SLEMicro, + Name: "6.0", + }, + }, + }, + { + name: "SUSE Linux Enterprise Micro 5.4 for Rancher", + inputFile: "testdata/slemicro-rancher", + want: &analyzer.AnalysisResult{ + OS: types.OS{ + Family: types.SLEMicro, + Name: "5.4", + }, + }, + }, { name: "Photon OS", inputFile: "testdata/photon", diff --git a/pkg/fanal/analyzer/os/release/testdata/slemicro b/pkg/fanal/analyzer/os/release/testdata/slemicro new file mode 100644 index 000000000000..3e3bdaa30de5 --- /dev/null +++ b/pkg/fanal/analyzer/os/release/testdata/slemicro @@ -0,0 +1,8 @@ +NAME="SLE Micro" +VERSION="5.3" +VERSION_ID="5.3" +PRETTY_NAME="SUSE Linux Enterprise Micro 5.3" +ID="sle-micro" +ID_LIKE="suse" +ANSI_COLOR="0;32" +CPE_NAME="cpe:/o:suse:sle-micro:5.3" diff --git a/pkg/fanal/analyzer/os/release/testdata/slemicro-rancher b/pkg/fanal/analyzer/os/release/testdata/slemicro-rancher new file mode 100644 index 000000000000..83ce34662c3d --- /dev/null +++ b/pkg/fanal/analyzer/os/release/testdata/slemicro-rancher @@ -0,0 +1,13 @@ +NAME="SLE Micro" +VERSION="5.4" +VERSION_ID="5.4" +PRETTY_NAME="SUSE Linux Enterprise Micro for Rancher 5.4" +ID="sle-micro-rancher" +ID_LIKE="suse" +ANSI_COLOR="0;32" +CPE_NAME="cpe:/o:suse:sle-micro-rancher:5.4" +IMAGE_REPO="registry.suse.com/rancher/elemental-teal/5.4" +IMAGE_TAG="1.2.3-3.2.153" +IMAGE="registry.suse.com/rancher/elemental-teal/5.4:1.2.3-3.2.153" +TIMESTAMP=20240419051540 +GRUB_ENTRY_NAME="Elemental" diff --git a/pkg/fanal/analyzer/os/release/testdata/slemicro6.0 b/pkg/fanal/analyzer/os/release/testdata/slemicro6.0 new file mode 100644 index 000000000000..a18632b128a6 --- /dev/null +++ b/pkg/fanal/analyzer/os/release/testdata/slemicro6.0 @@ -0,0 +1,10 @@ +NAME="SL-Micro" +VERSION="6.0" +VERSION_ID="6.0" +PRETTY_NAME="SUSE Linux Micro 6.0" +ID="sl-micro" +ID_LIKE="suse" +ANSI_COLOR="0;32" +CPE_NAME="cpe:/o:suse:sl-micro:6.0" +HOME_URL="https://www.suse.com/products/micro/" +DOCUMENTATION_URL="https://documentation.suse.com/sl-micro/6.0/" diff --git a/pkg/fanal/test/integration/testdata/goldens/packages/opensuse-tumbleweed.json.golden b/pkg/fanal/test/integration/testdata/goldens/packages/opensuse-tumbleweed.json.golden index a651f86e3862..fa00e70aa6e6 100644 --- a/pkg/fanal/test/integration/testdata/goldens/packages/opensuse-tumbleweed.json.golden +++ b/pkg/fanal/test/integration/testdata/goldens/packages/opensuse-tumbleweed.json.golden @@ -3,7 +3,7 @@ "ID": "aaa_base@84.87+git20240523.10a5692-1.1.x86_64", "Name": "aaa_base", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/aaa_base@84.87%2Bgit20240523.10a5692-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/aaa_base@84.87%2Bgit20240523.10a5692-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "fe755017155caefc" }, "Version": "84.87+git20240523.10a5692", @@ -34,7 +34,7 @@ "ID": "bash@5.2.26-12.1.x86_64", "Name": "bash", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/bash@5.2.26-12.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/bash@5.2.26-12.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "ce56393f87add219" }, "Version": "5.2.26", @@ -61,7 +61,7 @@ "ID": "bash-sh@5.2.26-12.1.noarch", "Name": "bash-sh", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/bash-sh@5.2.26-12.1?arch=noarch&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/bash-sh@5.2.26-12.1?arch=noarch&distro=opensuse.tumbleweed-20240607", "UID": "7363186d472571e0" }, "Version": "5.2.26", @@ -87,7 +87,7 @@ "ID": "boost-license1_85_0@1.85.0-1.2.noarch", "Name": "boost-license1_85_0", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/boost-license1_85_0@1.85.0-1.2?arch=noarch&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/boost-license1_85_0@1.85.0-1.2?arch=noarch&distro=opensuse.tumbleweed-20240607", "UID": "2d87c856df6862ee" }, "Version": "1.85.0", @@ -110,7 +110,7 @@ "ID": "branding-openSUSE@84.87.20240405-1.2.noarch", "Name": "branding-openSUSE", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/branding-openSUSE@84.87.20240405-1.2?arch=noarch&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/branding-openSUSE@84.87.20240405-1.2?arch=noarch&distro=opensuse.tumbleweed-20240607", "UID": "acda90b5f91cb463" }, "Version": "84.87.20240405", @@ -133,7 +133,7 @@ "ID": "ca-certificates@2+git20240415.3fe9324-1.1.noarch", "Name": "ca-certificates", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/ca-certificates@2%2Bgit20240415.3fe9324-1.1?arch=noarch&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/ca-certificates@2%2Bgit20240415.3fe9324-1.1?arch=noarch&distro=opensuse.tumbleweed-20240607", "UID": "7a18ce239fe8c044" }, "Version": "2+git20240415.3fe9324", @@ -162,7 +162,7 @@ "ID": "ca-certificates-mozilla@2.66-1.2.noarch", "Name": "ca-certificates-mozilla", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/ca-certificates-mozilla@2.66-1.2?arch=noarch&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/ca-certificates-mozilla@2.66-1.2?arch=noarch&distro=opensuse.tumbleweed-20240607", "UID": "362f343aa3c5d416" }, "Version": "2.66", @@ -189,7 +189,7 @@ "ID": "coreutils@9.5-1.1.x86_64", "Name": "coreutils", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/coreutils@9.5-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/coreutils@9.5-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "9483aa372c47866d" }, "Version": "9.5", @@ -221,7 +221,7 @@ "ID": "cracklib-dict-small@2.9.11-1.4.x86_64", "Name": "cracklib-dict-small", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/cracklib-dict-small@2.9.11-1.4?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/cracklib-dict-small@2.9.11-1.4?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "cc38aea883124e41" }, "Version": "2.9.11", @@ -244,7 +244,7 @@ "ID": "crypto-policies@20230920.570ea89-3.2.noarch", "Name": "crypto-policies", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/crypto-policies@20230920.570ea89-3.2?arch=noarch&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/crypto-policies@20230920.570ea89-3.2?arch=noarch&distro=opensuse.tumbleweed-20240607", "UID": "ebef887879b412aa" }, "Version": "20230920.570ea89", @@ -267,7 +267,7 @@ "ID": "curl@8.8.0-1.1.x86_64", "Name": "curl", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/curl@8.8.0-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/curl@8.8.0-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "50b6514bae052e62" }, "Version": "8.8.0", @@ -295,7 +295,7 @@ "ID": "file-magic@5.45-2.2.noarch", "Name": "file-magic", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/file-magic@5.45-2.2?arch=noarch&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/file-magic@5.45-2.2?arch=noarch&distro=opensuse.tumbleweed-20240607", "UID": "9318efe3deebc83a" }, "Version": "5.45", @@ -318,7 +318,7 @@ "ID": "filesystem@84.87-15.3.x86_64", "Name": "filesystem", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/filesystem@84.87-15.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/filesystem@84.87-15.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "379508af5bc6bae5" }, "Version": "84.87", @@ -344,7 +344,7 @@ "ID": "fillup@1.42-281.1.x86_64", "Name": "fillup", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/fillup@1.42-281.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/fillup@1.42-281.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "7d48bfb3846c8056" }, "Version": "1.42", @@ -370,7 +370,7 @@ "ID": "glibc@2.39-9.1.x86_64", "Name": "glibc", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/glibc@2.39-9.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/glibc@2.39-9.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "77433316d747193b" }, "Version": "2.39", @@ -396,7 +396,7 @@ "ID": "glibc-locale-base@2.39-9.1.x86_64", "Name": "glibc-locale-base", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/glibc-locale-base@2.39-9.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/glibc-locale-base@2.39-9.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "7f6f8a5c2e27af75" }, "Version": "2.39", @@ -422,7 +422,7 @@ "ID": "gpg-pubkey@29b700a4-62b07e22.", "Name": "gpg-pubkey", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/gpg-pubkey@29b700a4-62b07e22?arch=None&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/gpg-pubkey@29b700a4-62b07e22?arch=None&distro=opensuse.tumbleweed-20240607", "UID": "562934f3f56669a5" }, "Version": "29b700a4", @@ -440,7 +440,7 @@ "ID": "gpg-pubkey@39db7c82-510a966b.", "Name": "gpg-pubkey", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/gpg-pubkey@39db7c82-510a966b?arch=None&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/gpg-pubkey@39db7c82-510a966b?arch=None&distro=opensuse.tumbleweed-20240607", "UID": "5e72dadde79df0d4" }, "Version": "39db7c82", @@ -458,7 +458,7 @@ "ID": "gpg2@2.4.5-1.1.x86_64", "Name": "gpg2", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/gpg2@2.4.5-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/gpg2@2.4.5-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "e95cc1c58ec7e824" }, "Version": "2.4.5", @@ -496,7 +496,7 @@ "ID": "grep@3.11-3.1.x86_64", "Name": "grep", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/grep@3.11-3.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/grep@3.11-3.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "7c3b5ec5d53fa9f9" }, "Version": "3.11", @@ -524,7 +524,7 @@ "ID": "gzip@1.13-3.1.x86_64", "Name": "gzip", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/gzip@1.13-3.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/gzip@1.13-3.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "f51af60e831e41e" }, "Version": "1.13", @@ -551,7 +551,7 @@ "ID": "krb5@1.21.2-5.1.x86_64", "Name": "krb5", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/krb5@1.21.2-5.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/krb5@1.21.2-5.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "f22a7694d8a232ac" }, "Version": "1.21.2", @@ -583,7 +583,7 @@ "ID": "libabsl_lite_2401_0_0@20240116.2-2.1.x86_64", "Name": "libabsl_lite_2401_0_0", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libabsl_lite_2401_0_0@20240116.2-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libabsl_lite_2401_0_0@20240116.2-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "24307f175234d50" }, "Version": "20240116.2", @@ -611,7 +611,7 @@ "ID": "libacl1@2.3.2-2.1.x86_64", "Name": "libacl1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libacl1@2.3.2-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libacl1@2.3.2-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "6e55e249889869ed" }, "Version": "2.3.2", @@ -637,7 +637,7 @@ "ID": "libassuan0@2.5.7-1.1.x86_64", "Name": "libassuan0", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libassuan0@2.5.7-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libassuan0@2.5.7-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "4a9f149fc3b4d802" }, "Version": "2.5.7", @@ -664,7 +664,7 @@ "ID": "libattr1@2.5.2-1.2.x86_64", "Name": "libattr1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libattr1@2.5.2-1.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libattr1@2.5.2-1.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "bf6e596e053cc667" }, "Version": "2.5.2", @@ -690,7 +690,7 @@ "ID": "libaudit1@3.1.1-1.6.x86_64", "Name": "libaudit1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libaudit1@3.1.1-1.6?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libaudit1@3.1.1-1.6?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "c2ab09cc3b09bf56" }, "Version": "3.1.1", @@ -716,7 +716,7 @@ "ID": "libaugeas0@1.14.1-1.3.x86_64", "Name": "libaugeas0", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libaugeas0@1.14.1-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libaugeas0@1.14.1-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "bc9b541f623eec37" }, "Version": "1.14.1", @@ -744,7 +744,7 @@ "ID": "libblkid1@2.40.1-2.1.x86_64", "Name": "libblkid1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libblkid1@2.40.1-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libblkid1@2.40.1-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "bcf4491906d1eb4d" }, "Version": "2.40.1", @@ -771,7 +771,7 @@ "ID": "libboost_thread1_85_0@1.85.0-1.2.x86_64", "Name": "libboost_thread1_85_0", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libboost_thread1_85_0@1.85.0-1.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libboost_thread1_85_0@1.85.0-1.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "b8612fd1d8aa51a7" }, "Version": "1.85.0", @@ -800,7 +800,7 @@ "ID": "libbrotlicommon1@1.1.0-1.3.x86_64", "Name": "libbrotlicommon1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libbrotlicommon1@1.1.0-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libbrotlicommon1@1.1.0-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "f1d7b84b18abde08" }, "Version": "1.1.0", @@ -826,7 +826,7 @@ "ID": "libbrotlidec1@1.1.0-1.3.x86_64", "Name": "libbrotlidec1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libbrotlidec1@1.1.0-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libbrotlidec1@1.1.0-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "5c297a82e6701a0d" }, "Version": "1.1.0", @@ -853,7 +853,7 @@ "ID": "libbz2-1@1.0.8-5.10.x86_64", "Name": "libbz2-1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libbz2-1@1.0.8-5.10?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libbz2-1@1.0.8-5.10?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "702f3dd378cba8f0" }, "Version": "1.0.8", @@ -879,7 +879,7 @@ "ID": "libcap-ng0@0.8.5-1.1.x86_64", "Name": "libcap-ng0", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libcap-ng0@0.8.5-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libcap-ng0@0.8.5-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "b40d6fdd09912405" }, "Version": "0.8.5", @@ -905,7 +905,7 @@ "ID": "libcap2@2.70-1.1.x86_64", "Name": "libcap2", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libcap2@2.70-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libcap2@2.70-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "c33018bbf8c4bdfa" }, "Version": "2.70", @@ -931,7 +931,7 @@ "ID": "libcom_err2@1.47.0-4.2.x86_64", "Name": "libcom_err2", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libcom_err2@1.47.0-4.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libcom_err2@1.47.0-4.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "58b023020895cfea" }, "Version": "1.47.0", @@ -957,7 +957,7 @@ "ID": "libcrypt1@4.4.36-1.6.x86_64", "Name": "libcrypt1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libcrypt1@4.4.36-1.6?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libcrypt1@4.4.36-1.6?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "541be9a801034440" }, "Version": "4.4.36", @@ -983,7 +983,7 @@ "ID": "libcurl4@8.8.0-1.1.x86_64", "Name": "libcurl4", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libcurl4@8.8.0-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libcurl4@8.8.0-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "59c3c7a8962c110a" }, "Version": "8.8.0", @@ -1019,7 +1019,7 @@ "ID": "libeconf0@0.6.3-1.1.x86_64", "Name": "libeconf0", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libeconf0@0.6.3-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libeconf0@0.6.3-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "9e3e97464bc6164b" }, "Version": "0.6.3", @@ -1045,7 +1045,7 @@ "ID": "libfa1@1.14.1-1.3.x86_64", "Name": "libfa1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libfa1@1.14.1-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libfa1@1.14.1-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "9df420b84b79a62" }, "Version": "1.14.1", @@ -1071,7 +1071,7 @@ "ID": "libfdisk1@2.40.1-2.1.x86_64", "Name": "libfdisk1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libfdisk1@2.40.1-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libfdisk1@2.40.1-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "ab47b44e7c45eab1" }, "Version": "2.40.1", @@ -1099,7 +1099,7 @@ "ID": "libffi8@3.4.6-1.1.x86_64", "Name": "libffi8", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libffi8@3.4.6-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libffi8@3.4.6-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "a569681a5276bde6" }, "Version": "3.4.6", @@ -1125,7 +1125,7 @@ "ID": "libgcc_s1@14.1.0+git10173-1.1.x86_64", "Name": "libgcc_s1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libgcc_s1@14.1.0%2Bgit10173-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libgcc_s1@14.1.0%2Bgit10173-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "3130b825fbc3a81e" }, "Version": "14.1.0+git10173", @@ -1151,7 +1151,7 @@ "ID": "libgcrypt20@1.10.3-3.3.x86_64", "Name": "libgcrypt20", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libgcrypt20@1.10.3-3.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libgcrypt20@1.10.3-3.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "ac38e6e75132d1c6" }, "Version": "1.10.3", @@ -1178,7 +1178,7 @@ "ID": "libglib-2_0-0@2.80.2-1.1.x86_64", "Name": "libglib-2_0-0", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libglib-2_0-0@2.80.2-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libglib-2_0-0@2.80.2-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "e3dccc27a6f44a3d" }, "Version": "2.80.2", @@ -1205,7 +1205,7 @@ "ID": "libgmp10@6.3.0-3.2.x86_64", "Name": "libgmp10", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libgmp10@6.3.0-3.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libgmp10@6.3.0-3.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "fb3994e26d59ae4f" }, "Version": "6.3.0", @@ -1231,7 +1231,7 @@ "ID": "libgpg-error0@1.49-1.1.x86_64", "Name": "libgpg-error0", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libgpg-error0@1.49-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libgpg-error0@1.49-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "a3b16ea69b05fe60" }, "Version": "1.49", @@ -1257,7 +1257,7 @@ "ID": "libgpgme11@1.23.2-4.2.x86_64", "Name": "libgpgme11", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libgpgme11@1.23.2-4.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libgpgme11@1.23.2-4.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "6d9271ab523fb009" }, "Version": "1.23.2", @@ -1286,7 +1286,7 @@ "ID": "libidn2-0@2.3.7-1.2.x86_64", "Name": "libidn2-0", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libidn2-0@2.3.7-1.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libidn2-0@2.3.7-1.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "ae81c3e9fc0d0fc3" }, "Version": "2.3.7", @@ -1313,7 +1313,7 @@ "ID": "libkeyutils1@1.6.3-7.2.x86_64", "Name": "libkeyutils1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libkeyutils1@1.6.3-7.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libkeyutils1@1.6.3-7.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "f9f931edfe4b540c" }, "Version": "1.6.3", @@ -1339,7 +1339,7 @@ "ID": "libksba8@1.6.6-1.1.x86_64", "Name": "libksba8", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libksba8@1.6.6-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libksba8@1.6.6-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "c532eef98bb36938" }, "Version": "1.6.6", @@ -1366,7 +1366,7 @@ "ID": "libldap2@2.6.7-2.1.x86_64", "Name": "libldap2", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libldap2@2.6.7-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libldap2@2.6.7-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "55fa8e45be9ed78" }, "Version": "2.6.7", @@ -1394,7 +1394,7 @@ "ID": "liblua5_4-5@5.4.6-3.3.x86_64", "Name": "liblua5_4-5", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/liblua5_4-5@5.4.6-3.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/liblua5_4-5@5.4.6-3.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "98b4001b2f59f46" }, "Version": "5.4.6", @@ -1420,7 +1420,7 @@ "ID": "liblz4-1@1.9.4-2.8.x86_64", "Name": "liblz4-1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/liblz4-1@1.9.4-2.8?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/liblz4-1@1.9.4-2.8?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "267a6bfb140f0d45" }, "Version": "1.9.4", @@ -1446,7 +1446,7 @@ "ID": "liblzma5@5.6.2-1.1.x86_64", "Name": "liblzma5", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/liblzma5@5.6.2-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/liblzma5@5.6.2-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "304510f1f6669e2c" }, "Version": "5.6.2", @@ -1472,7 +1472,7 @@ "ID": "libmagic1@5.45-2.2.x86_64", "Name": "libmagic1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libmagic1@5.45-2.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libmagic1@5.45-2.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "d8fdc2934df34a83" }, "Version": "5.45", @@ -1503,7 +1503,7 @@ "ID": "libmount1@2.40.1-2.1.x86_64", "Name": "libmount1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libmount1@2.40.1-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libmount1@2.40.1-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "8386ec24a06557ea" }, "Version": "2.40.1", @@ -1531,7 +1531,7 @@ "ID": "libncurses6@6.5.20240601-38.1.x86_64", "Name": "libncurses6", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libncurses6@6.5.20240601-38.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libncurses6@6.5.20240601-38.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "9513bf16199cee6b" }, "Version": "6.5.20240601", @@ -1560,7 +1560,7 @@ "ID": "libnghttp2-14@1.61.0-1.1.x86_64", "Name": "libnghttp2-14", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libnghttp2-14@1.61.0-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libnghttp2-14@1.61.0-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "de28696676fc1ebd" }, "Version": "1.61.0", @@ -1586,7 +1586,7 @@ "ID": "libnpth0@1.7-1.1.x86_64", "Name": "libnpth0", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libnpth0@1.7-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libnpth0@1.7-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "7bff27e583fb62b3" }, "Version": "1.7", @@ -1612,7 +1612,7 @@ "ID": "libnss_usrfiles2@2.27.1-1.2.x86_64", "Name": "libnss_usrfiles2", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libnss_usrfiles2@2.27.1-1.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libnss_usrfiles2@2.27.1-1.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "d3c8c8f840c86b12" }, "Version": "2.27.1", @@ -1638,7 +1638,7 @@ "ID": "libopenssl-3-fips-provider@3.1.4-9.1.x86_64", "Name": "libopenssl-3-fips-provider", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libopenssl-3-fips-provider@3.1.4-9.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libopenssl-3-fips-provider@3.1.4-9.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "65c56c2870042412" }, "Version": "3.1.4", @@ -1665,7 +1665,7 @@ "ID": "libopenssl3@3.1.4-9.1.x86_64", "Name": "libopenssl3", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libopenssl3@3.1.4-9.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libopenssl3@3.1.4-9.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "f051425f385d2b99" }, "Version": "3.1.4", @@ -1693,7 +1693,7 @@ "ID": "libp11-kit0@0.25.3-1.3.x86_64", "Name": "libp11-kit0", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libp11-kit0@0.25.3-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libp11-kit0@0.25.3-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "fbca9a69218ce8e7" }, "Version": "0.25.3", @@ -1720,7 +1720,7 @@ "ID": "libpcre2-8-0@10.43-3.1.x86_64", "Name": "libpcre2-8-0", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libpcre2-8-0@10.43-3.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libpcre2-8-0@10.43-3.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "dabdfbc56d214ae6" }, "Version": "10.43", @@ -1746,7 +1746,7 @@ "ID": "libpopt0@1.19-1.8.x86_64", "Name": "libpopt0", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libpopt0@1.19-1.8?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libpopt0@1.19-1.8?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "98fa32fcd9ee1e39" }, "Version": "1.19", @@ -1772,7 +1772,7 @@ "ID": "libprocps8@3.3.17-17.1.x86_64", "Name": "libprocps8", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libprocps8@3.3.17-17.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libprocps8@3.3.17-17.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "f874f4997e1438be" }, "Version": "3.3.17", @@ -1799,7 +1799,7 @@ "ID": "libprotobuf-lite25_3_0@25.3-11.2.x86_64", "Name": "libprotobuf-lite25_3_0", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libprotobuf-lite25_3_0@25.3-11.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libprotobuf-lite25_3_0@25.3-11.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "b306bfd6494e6405" }, "Version": "25.3", @@ -1828,7 +1828,7 @@ "ID": "libpsl5@0.21.5-1.2.x86_64", "Name": "libpsl5", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libpsl5@0.21.5-1.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libpsl5@0.21.5-1.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "5d2411f7ede68692" }, "Version": "0.21.5", @@ -1856,7 +1856,7 @@ "ID": "libreadline8@8.2.10-1.3.x86_64", "Name": "libreadline8", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libreadline8@8.2.10-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libreadline8@8.2.10-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "9271e2cd0119054c" }, "Version": "8.2.10", @@ -1883,7 +1883,7 @@ "ID": "libsasl2-3@2.1.28-8.1.x86_64", "Name": "libsasl2-3", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libsasl2-3@2.1.28-8.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libsasl2-3@2.1.28-8.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "fe2536ad8601f334" }, "Version": "2.1.28", @@ -1909,7 +1909,7 @@ "ID": "libselinux1@3.6-1.3.x86_64", "Name": "libselinux1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libselinux1@3.6-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libselinux1@3.6-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "6bc8fe60a073ba96" }, "Version": "3.6", @@ -1936,7 +1936,7 @@ "ID": "libsemanage-conf@3.6-2.1.x86_64", "Name": "libsemanage-conf", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libsemanage-conf@3.6-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libsemanage-conf@3.6-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "56c91988ca2e8ce5" }, "Version": "3.6", @@ -1959,7 +1959,7 @@ "ID": "libsemanage2@3.6-2.1.x86_64", "Name": "libsemanage2", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libsemanage2@3.6-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libsemanage2@3.6-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "d945b0271ed45cf5" }, "Version": "3.6", @@ -1990,7 +1990,7 @@ "ID": "libsepol2@3.6-1.3.x86_64", "Name": "libsepol2", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libsepol2@3.6-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libsepol2@3.6-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "f2aaf81754d3169d" }, "Version": "3.6", @@ -2016,7 +2016,7 @@ "ID": "libsigc-2_0-0@2.12.1-2.3.x86_64", "Name": "libsigc-2_0-0", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libsigc-2_0-0@2.12.1-2.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libsigc-2_0-0@2.12.1-2.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "c4d52d6f33dee391" }, "Version": "2.12.1", @@ -2044,7 +2044,7 @@ "ID": "libsmartcols1@2.40.1-2.1.x86_64", "Name": "libsmartcols1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libsmartcols1@2.40.1-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libsmartcols1@2.40.1-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "5302abe63411170d" }, "Version": "2.40.1", @@ -2070,7 +2070,7 @@ "ID": "libsolv-tools-base@0.7.29-1.1.x86_64", "Name": "libsolv-tools-base", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libsolv-tools-base@0.7.29-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libsolv-tools-base@0.7.29-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "f2adb3efc201c696" }, "Version": "0.7.29", @@ -2102,7 +2102,7 @@ "ID": "libsqlite3-0@3.46.0-1.1.x86_64", "Name": "libsqlite3-0", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libsqlite3-0@3.46.0-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libsqlite3-0@3.46.0-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "d9bf1a49d16f0c" }, "Version": "3.46.0", @@ -2128,7 +2128,7 @@ "ID": "libssh-config@0.10.6-2.1.x86_64", "Name": "libssh-config", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libssh-config@0.10.6-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libssh-config@0.10.6-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "8628d51e34c2f5b1" }, "Version": "0.10.6", @@ -2151,7 +2151,7 @@ "ID": "libssh4@0.10.6-2.1.x86_64", "Name": "libssh4", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libssh4@0.10.6-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libssh4@0.10.6-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "d07880785aee16c8" }, "Version": "0.10.6", @@ -2181,7 +2181,7 @@ "ID": "libstdc++6@14.1.0+git10173-1.1.x86_64", "Name": "libstdc++6", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libstdc%2B%2B6@14.1.0%2Bgit10173-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libstdc%2B%2B6@14.1.0%2Bgit10173-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "f3345c3d3261e7e9" }, "Version": "14.1.0+git10173", @@ -2208,7 +2208,7 @@ "ID": "libsubid4@4.15.1-1.2.x86_64", "Name": "libsubid4", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libsubid4@4.15.1-1.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libsubid4@4.15.1-1.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "e155b313aa6da812" }, "Version": "4.15.1", @@ -2236,7 +2236,7 @@ "ID": "libsystemd0@255.7-2.1.x86_64", "Name": "libsystemd0", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libsystemd0@255.7-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libsystemd0@255.7-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "4fa3c2608f054287" }, "Version": "255.7", @@ -2267,7 +2267,7 @@ "ID": "libtasn1-6@4.19.0-1.7.x86_64", "Name": "libtasn1-6", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libtasn1-6@4.19.0-1.7?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libtasn1-6@4.19.0-1.7?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "35e287fcdf033bd1" }, "Version": "4.19.0", @@ -2293,7 +2293,7 @@ "ID": "libudev1@255.7-2.1.x86_64", "Name": "libudev1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libudev1@255.7-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libudev1@255.7-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "4ae1c62105f1f901" }, "Version": "255.7", @@ -2320,7 +2320,7 @@ "ID": "libunistring5@1.2-1.1.x86_64", "Name": "libunistring5", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libunistring5@1.2-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libunistring5@1.2-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "e8be56f8ad59a760" }, "Version": "1.2", @@ -2346,7 +2346,7 @@ "ID": "libusb-1_0-0@1.0.27-1.2.x86_64", "Name": "libusb-1_0-0", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libusb-1_0-0@1.0.27-1.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libusb-1_0-0@1.0.27-1.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "dab90c8d517b4ee4" }, "Version": "1.0.27", @@ -2373,7 +2373,7 @@ "ID": "libuuid1@2.40.1-2.1.x86_64", "Name": "libuuid1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libuuid1@2.40.1-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libuuid1@2.40.1-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "bc5c46e1650d4a95" }, "Version": "2.40.1", @@ -2399,7 +2399,7 @@ "ID": "libverto1@0.3.2-3.3.x86_64", "Name": "libverto1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libverto1@0.3.2-3.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libverto1@0.3.2-3.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "8c13b7ac8ed99616" }, "Version": "0.3.2", @@ -2425,7 +2425,7 @@ "ID": "libxml2-2@2.12.7-1.1.x86_64", "Name": "libxml2-2", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libxml2-2@2.12.7-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libxml2-2@2.12.7-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "1285499ab636c5d9" }, "Version": "2.12.7", @@ -2453,7 +2453,7 @@ "ID": "libyaml-cpp0_8@0.8.0-1.3.x86_64", "Name": "libyaml-cpp0_8", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libyaml-cpp0_8@0.8.0-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libyaml-cpp0_8@0.8.0-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "d743795a2d65f87b" }, "Version": "0.8.0", @@ -2481,7 +2481,7 @@ "ID": "libz1@1.3.1-1.1.x86_64", "Name": "libz1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libz1@1.3.1-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libz1@1.3.1-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "f09857fffac622a" }, "Version": "1.3.1", @@ -2507,7 +2507,7 @@ "ID": "libzck1@1.4.0-2.1.x86_64", "Name": "libzck1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libzck1@1.4.0-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libzck1@1.4.0-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "76b3d8e58402a974" }, "Version": "1.4.0", @@ -2535,7 +2535,7 @@ "ID": "libzstd1@1.5.6-1.1.x86_64", "Name": "libzstd1", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libzstd1@1.5.6-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libzstd1@1.5.6-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "4edc1117cd2019eb" }, "Version": "1.5.6", @@ -2561,7 +2561,7 @@ "ID": "libzypp@17.34.1-1.1.x86_64", "Name": "libzypp", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/libzypp@17.34.1-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/libzypp@17.34.1-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "3545239e91f3bd9" }, "Version": "17.34.1", @@ -2606,7 +2606,7 @@ "ID": "login_defs@4.15.1-1.2.noarch", "Name": "login_defs", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/login_defs@4.15.1-1.2?arch=noarch&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/login_defs@4.15.1-1.2?arch=noarch&distro=opensuse.tumbleweed-20240607", "UID": "1695371f9551a301" }, "Version": "4.15.1", @@ -2632,7 +2632,7 @@ "ID": "lsb-release@3.3-1.3.noarch", "Name": "lsb-release", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/lsb-release@3.3-1.3?arch=noarch&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/lsb-release@3.3-1.3?arch=noarch&distro=opensuse.tumbleweed-20240607", "UID": "8c82a3a248c52a13" }, "Version": "3.3", @@ -2659,7 +2659,7 @@ "ID": "ncurses-utils@6.5.20240601-38.1.x86_64", "Name": "ncurses-utils", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/ncurses-utils@6.5.20240601-38.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/ncurses-utils@6.5.20240601-38.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "90d23a67ceb37784" }, "Version": "6.5.20240601", @@ -2686,7 +2686,7 @@ "ID": "netcfg@11.6-13.3.noarch", "Name": "netcfg", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/netcfg@11.6-13.3?arch=noarch&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/netcfg@11.6-13.3?arch=noarch&distro=opensuse.tumbleweed-20240607", "UID": "c32526003d9c5528" }, "Version": "11.6", @@ -2712,7 +2712,7 @@ "ID": "openSUSE-build-key@1.0-53.1.x86_64", "Name": "openSUSE-build-key", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/openSUSE-build-key@1.0-53.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/openSUSE-build-key@1.0-53.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "ed8309d0e84993e4" }, "Version": "1.0", @@ -2739,7 +2739,7 @@ "ID": "openSUSE-release@20240607-2943.1.x86_64", "Name": "openSUSE-release", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/openSUSE-release@20240607-2943.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/openSUSE-release@20240607-2943.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "ad908712f8c8e5ab" }, "Version": "20240607", @@ -2766,7 +2766,7 @@ "ID": "openSUSE-release-appliance-docker@20240607-2943.1.x86_64", "Name": "openSUSE-release-appliance-docker", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/openSUSE-release-appliance-docker@20240607-2943.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/openSUSE-release-appliance-docker@20240607-2943.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "46f06026407817a0" }, "Version": "20240607", @@ -2789,7 +2789,7 @@ "ID": "openssl@3.1.4-3.2.noarch", "Name": "openssl", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/openssl@3.1.4-3.2?arch=noarch&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/openssl@3.1.4-3.2?arch=noarch&distro=opensuse.tumbleweed-20240607", "UID": "cd2ead77021cf857" }, "Version": "3.1.4", @@ -2815,7 +2815,7 @@ "ID": "openssl-3@3.1.4-9.1.x86_64", "Name": "openssl-3", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/openssl-3@3.1.4-9.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/openssl-3@3.1.4-9.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "da148866e5ba5d92" }, "Version": "3.1.4", @@ -2846,7 +2846,7 @@ "ID": "p11-kit@0.25.3-1.3.x86_64", "Name": "p11-kit", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/p11-kit@0.25.3-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/p11-kit@0.25.3-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "7da38dbf3cd84149" }, "Version": "0.25.3", @@ -2874,7 +2874,7 @@ "ID": "p11-kit-tools@0.25.3-1.3.x86_64", "Name": "p11-kit-tools", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/p11-kit-tools@0.25.3-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/p11-kit-tools@0.25.3-1.3?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "fb534863cc7b3050" }, "Version": "0.25.3", @@ -2902,7 +2902,7 @@ "ID": "pam@1.6.1-1.1.x86_64", "Name": "pam", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/pam@1.6.1-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/pam@1.6.1-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "2cc82a7c85091dc0" }, "Version": "1.6.1", @@ -2935,7 +2935,7 @@ "ID": "patterns-base-fips@20200505-51.1.x86_64", "Name": "patterns-base-fips", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/patterns-base-fips@20200505-51.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/patterns-base-fips@20200505-51.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "70a74594ade38509" }, "Version": "20200505", @@ -2958,7 +2958,7 @@ "ID": "patterns-base-minimal_base@20200505-51.1.x86_64", "Name": "patterns-base-minimal_base", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/patterns-base-minimal_base@20200505-51.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/patterns-base-minimal_base@20200505-51.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "22550c4b68de6581" }, "Version": "20200505", @@ -2987,7 +2987,7 @@ "ID": "permctl@1699_20240522-1.1.x86_64", "Name": "permctl", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/permctl@1699_20240522-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/permctl@1699_20240522-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "cfcd9931dafbea39" }, "Version": "1699_20240522", @@ -3016,7 +3016,7 @@ "ID": "permissions@1699_20240522-1.1.x86_64", "Name": "permissions", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/permissions@1699_20240522-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/permissions@1699_20240522-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "971d93fae8da6b23" }, "Version": "1699_20240522", @@ -3043,7 +3043,7 @@ "ID": "permissions-config@1699_20240522-1.1.noarch", "Name": "permissions-config", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/permissions-config@1699_20240522-1.1?arch=noarch&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/permissions-config@1699_20240522-1.1?arch=noarch&distro=opensuse.tumbleweed-20240607", "UID": "8bd3994be34b3e73" }, "Version": "1699_20240522", @@ -3072,7 +3072,7 @@ "ID": "pinentry@1.2.1-3.5.x86_64", "Name": "pinentry", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/pinentry@1.2.1-3.5?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/pinentry@1.2.1-3.5?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "90686edea2822ef8" }, "Version": "1.2.1", @@ -3102,7 +3102,7 @@ "ID": "procps@3.3.17-17.1.x86_64", "Name": "procps", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/procps@3.3.17-17.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/procps@3.3.17-17.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "41a25e357a85fe17" }, "Version": "3.3.17", @@ -3131,7 +3131,7 @@ "ID": "rpm@4.19.1.1-3.2.x86_64", "Name": "rpm", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/rpm@4.19.1.1-3.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/rpm@4.19.1.1-3.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "6385ed7e7827135a" }, "Version": "4.19.1.1", @@ -3170,7 +3170,7 @@ "ID": "rpm-config-SUSE@20240214-1.2.noarch", "Name": "rpm-config-SUSE", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/rpm-config-SUSE@20240214-1.2?arch=noarch&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/rpm-config-SUSE@20240214-1.2?arch=noarch&distro=opensuse.tumbleweed-20240607", "UID": "b0a53b3b9cd8de6e" }, "Version": "20240214", @@ -3198,7 +3198,7 @@ "ID": "sed@4.9-2.6.x86_64", "Name": "sed", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/sed@4.9-2.6?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/sed@4.9-2.6?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "465c6c9c97824acd" }, "Version": "4.9", @@ -3226,7 +3226,7 @@ "ID": "shadow@4.15.1-1.2.x86_64", "Name": "shadow", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/shadow@4.15.1-1.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/shadow@4.15.1-1.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "7fefaa914168ef4f" }, "Version": "4.15.1", @@ -3265,7 +3265,7 @@ "ID": "system-user-root@20190513-2.16.noarch", "Name": "system-user-root", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/system-user-root@20190513-2.16?arch=noarch&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/system-user-root@20190513-2.16?arch=noarch&distro=opensuse.tumbleweed-20240607", "UID": "cc450033801f0db5" }, "Version": "20190513", @@ -3288,7 +3288,7 @@ "ID": "tar@1.35-2.2.x86_64", "Name": "tar", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/tar@1.35-2.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/tar@1.35-2.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "6f7d60b91f9b815f" }, "Version": "1.35", @@ -3316,7 +3316,7 @@ "ID": "terminfo-base@6.5.20240601-38.1.x86_64", "Name": "terminfo-base", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/terminfo-base@6.5.20240601-38.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/terminfo-base@6.5.20240601-38.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "ba53240ca965e6c0" }, "Version": "6.5.20240601", @@ -3342,7 +3342,7 @@ "ID": "timezone@2024a-3.2.x86_64", "Name": "timezone", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/timezone@2024a-3.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/timezone@2024a-3.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "aa7fc225c615b895" }, "Version": "2024a", @@ -3369,7 +3369,7 @@ "ID": "util-linux@2.40.1-2.1.x86_64", "Name": "util-linux", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/util-linux@2.40.1-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/util-linux@2.40.1-2.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "1440e3eb3dfc6c5" }, "Version": "2.40.1", @@ -3412,7 +3412,7 @@ "ID": "xz@5.6.2-1.1.x86_64", "Name": "xz", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/xz@5.6.2-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/xz@5.6.2-1.1?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "1c46963e750a4a9" }, "Version": "5.6.2", @@ -3440,7 +3440,7 @@ "ID": "zypper@1.14.73-1.2.x86_64", "Name": "zypper", "Identifier": { - "PURL": "pkg:rpm/opensuse.tumbleweed/zypper@1.14.73-1.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", + "PURL": "pkg:rpm/opensuse/zypper@1.14.73-1.2?arch=x86_64&distro=opensuse.tumbleweed-20240607", "UID": "9d7cafcab0f1fed2" }, "Version": "1.14.73", diff --git a/pkg/fanal/types/const.go b/pkg/fanal/types/const.go index ffe1e0718764..c304f40bac5f 100644 --- a/pkg/fanal/types/const.go +++ b/pkg/fanal/types/const.go @@ -37,6 +37,7 @@ const ( Photon OSType = "photon" RedHat OSType = "redhat" Rocky OSType = "rocky" + SLEMicro OSType = "suse linux enterprise micro" SLES OSType = "suse linux enterprise server" Ubuntu OSType = "ubuntu" Wolfi OSType = "wolfi" diff --git a/pkg/purl/purl.go b/pkg/purl/purl.go index d9bff7b11e15..a7b1cb9181f0 100644 --- a/pkg/purl/purl.go +++ b/pkg/purl/purl.go @@ -358,16 +358,19 @@ func parseRPM(fos *ftypes.OS, modularityLabel string) (ftypes.OSType, packageurl return "", packageurl.Qualifiers{} } - // SLES string has whitespace family := fos.Family - if fos.Family == ftypes.SLES { - family = "sles" + // SLES string has whitespace, also highlevel family is not the same as distro + if fos.Family == ftypes.SLES || fos.Family == ftypes.SLEMicro { + family = "suse" + } + if fos.Family == ftypes.OpenSUSETumbleweed || fos.Family == ftypes.OpenSUSELeap { + family = "opensuse" } qualifiers := packageurl.Qualifiers{ { Key: "distro", - Value: fmt.Sprintf("%s-%s", family, fos.Name), + Value: fmt.Sprintf("%s-%s", fos.Family, fos.Name), }, } @@ -476,7 +479,7 @@ func purlType(t ftypes.TargetType) string { return packageurl.TypeDebian case ftypes.RedHat, ftypes.CentOS, ftypes.Rocky, ftypes.Alma, ftypes.Amazon, ftypes.Fedora, ftypes.Oracle, ftypes.OpenSUSE, - ftypes.OpenSUSELeap, ftypes.OpenSUSETumbleweed, ftypes.SLES, ftypes.Photon, + ftypes.OpenSUSELeap, ftypes.OpenSUSETumbleweed, ftypes.SLES, ftypes.SLEMicro, ftypes.Photon, ftypes.Azure, ftypes.CBLMariner: return packageurl.TypeRPM case TypeOCI: From de40df9408d6d856a3ad384ec9f086edce3aa382 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Mon, 30 Sep 2024 10:50:53 +0600 Subject: [PATCH 004/108] fix(misconf): disable DS016 check for image history analyzer (#7540) Signed-off-by: nikpivkin --- .../analyzer/imgconf/dockerfile/dockerfile.go | 5 +++ .../imgconf/dockerfile/dockerfile_test.go | 41 +++++++++++++++++++ pkg/misconf/scanner.go | 3 ++ 3 files changed, 49 insertions(+) diff --git a/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go b/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go index 5a974a8dd1d1..c79a81f54c61 100644 --- a/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go +++ b/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go @@ -16,6 +16,10 @@ import ( "github.com/aquasecurity/trivy/pkg/misconf" ) +var disabledChecks = []string{ + "DS016", // See https://github.com/aquasecurity/trivy/issues/7368 +} + const analyzerVersion = 1 func init() { @@ -27,6 +31,7 @@ type historyAnalyzer struct { } func newHistoryAnalyzer(opts analyzer.ConfigAnalyzerOptions) (analyzer.ConfigAnalyzer, error) { + opts.MisconfScannerOption.DisabledCheckIDs = append(opts.MisconfScannerOption.DisabledCheckIDs, disabledChecks...) s, err := misconf.NewScanner(detection.FileTypeDockerfile, opts.MisconfScannerOption) if err != nil { return nil, xerrors.Errorf("misconfiguration scanner error: %w", err) diff --git a/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile_test.go b/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile_test.go index 1ee81016ac8c..c454de8ba3d4 100644 --- a/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile_test.go +++ b/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile_test.go @@ -284,6 +284,47 @@ func Test_historyAnalyzer_Analyze(t *testing.T) { Config: nil, }, }, + { + name: "DS016 check not detected", + input: analyzer.ConfigAnalysisInput{ + Config: &v1.ConfigFile{ + Config: v1.Config{ + Healthcheck: &v1.HealthConfig{ + Test: []string{"CMD-SHELL", "curl --fail http://localhost:3000 || exit 1"}, + Interval: time.Second * 10, + Timeout: time.Second * 3, + }, + }, + History: []v1.History{ + { + // duplicate command from another layer + CreatedBy: `/bin/sh -c #(nop) CMD [\"/bin/bash\"]`, + EmptyLayer: true, + }, + { + CreatedBy: "/bin/sh -c #(nop) ADD file:e4d600fc4c9c293efe360be7b30ee96579925d1b4634c94332e2ec73f7d8eca1 in /", + }, + { + CreatedBy: `HEALTHCHECK &{["CMD-SHELL" "curl --fail http://localhost:3000 || exit 1"] "10s" "3s" "0s" '\x00'}`, + }, + { + CreatedBy: `USER user`, + EmptyLayer: true, + }, + { + CreatedBy: `/bin/sh -c #(nop) CMD [\"/bin/sh\"]`, + EmptyLayer: true, + }, + }, + }, + }, + want: &analyzer.ConfigAnalysisResult{ + Misconfiguration: &types.Misconfiguration{ + FileType: types.Dockerfile, + FilePath: "Dockerfile", + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/misconf/scanner.go b/pkg/misconf/scanner.go index db203364a3e4..78f4f4bdb158 100644 --- a/pkg/misconf/scanner.go +++ b/pkg/misconf/scanner.go @@ -74,6 +74,8 @@ type ScannerOption struct { FilePatterns []string ConfigFileSchemas []*ConfigFileSchema + + DisabledCheckIDs []string } func (o *ScannerOption) Sort() { @@ -212,6 +214,7 @@ func scannerOptions(t detection.FileType, opt ScannerOption) ([]options.ScannerO rego.WithEmbeddedPolicies(!opt.DisableEmbeddedPolicies), rego.WithEmbeddedLibraries(!opt.DisableEmbeddedLibraries), options.ScannerWithIncludeDeprecatedChecks(opt.IncludeDeprecatedChecks), + rego.WithDisabledCheckIDs(opt.DisabledCheckIDs...), } policyFS, policyPaths, err := CreatePolicyFS(opt.PolicyPaths) From cb16d43b696284e7256adc4fff1ff108d1c7bff3 Mon Sep 17 00:00:00 2001 From: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> Date: Mon, 30 Sep 2024 16:42:12 +0600 Subject: [PATCH 005/108] ci: split `save` and `restore` cache actions (#7614) --- .github/workflows/cache-test-images.yaml | 84 ++++++++++++++++++++++++ .github/workflows/test.yaml | 9 +-- 2 files changed, 87 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/cache-test-images.yaml diff --git a/.github/workflows/cache-test-images.yaml b/.github/workflows/cache-test-images.yaml new file mode 100644 index 000000000000..da8aff10cfd2 --- /dev/null +++ b/.github/workflows/cache-test-images.yaml @@ -0,0 +1,84 @@ +name: Cache test images +on: + schedule: + - cron: "0 0 * * *" # Run this workflow every day at 00:00 to avoid cache deletion. + workflow_dispatch: + +jobs: + test-images: + name: Cache test images + runs-on: ubuntu-latest + steps: + - name: Check out code into the Go module directory + uses: actions/checkout@v4.1.6 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Install tools + uses: aquaproj/aqua-installer@v3.0.1 + with: + aqua_version: v1.25.0 + + - name: Generate image list digest + if: github.ref_name == 'main' + id: image-digest + run: | + IMAGE_LIST=$(skopeo list-tags docker://ghcr.io/aquasecurity/trivy-test-images) + DIGEST=$(echo "$IMAGE_LIST" | sha256sum | cut -d' ' -f1) + echo "digest=$DIGEST" >> $GITHUB_OUTPUT + + ## We need to work with test image cache only for main branch + - name: Restore and save test images cache + if: github.ref_name == 'main' + uses: actions/cache@v4 + with: + path: integration/testdata/fixtures/images + key: cache-test-images-${{ steps.image-digest.outputs.digest }} + restore-keys: + cache-test-images- + + - name: Download test images + if: github.ref_name == 'main' + run: mage test:fixtureContainerImages + + test-vm-images: + name: Cache test VM images + runs-on: ubuntu-latest + steps: + - name: Check out code into the Go module directory + uses: actions/checkout@v4.1.6 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Install tools + uses: aquaproj/aqua-installer@v3.0.1 + with: + aqua_version: v1.25.0 + + - name: Generate image list digest + if: github.ref_name == 'main' + id: image-digest + run: | + IMAGE_LIST=$(skopeo list-tags docker://ghcr.io/aquasecurity/trivy-test-vm-images) + DIGEST=$(echo "$IMAGE_LIST" | sha256sum | cut -d' ' -f1) + echo "digest=$DIGEST" >> $GITHUB_OUTPUT + + ## We need to work with test VM image cache only for main branch + - name: Restore and save test VM images cache + if: github.ref_name == 'main' + uses: actions/cache@v4 + with: + path: integration/testdata/fixtures/vm-images + key: cache-test-vm-images-${{ steps.image-digest.outputs.digest }} + restore-keys: + cache-test-vm-images- + + - name: Download test VM images + if: github.ref_name == 'main' + run: mage test:fixtureVMImages \ No newline at end of file diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index dc7d825af1c8..b423b61699fb 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -92,8 +92,7 @@ jobs: echo "digest=$DIGEST" >> $GITHUB_OUTPUT - name: Restore test images from cache - uses: actions/cache@v4 - id: restore-test-images + uses: actions/cache/restore@v4 with: path: integration/testdata/fixtures/images key: cache-test-images-${{ steps.image-digest.outputs.digest }} @@ -148,8 +147,7 @@ jobs: echo "digest=$DIGEST" >> $GITHUB_OUTPUT - name: Restore test images from cache - uses: actions/cache@v4 - id: restore-test-images + uses: actions/cache/restore@v4 with: path: integration/testdata/fixtures/images key: cache-test-images-${{ steps.image-digest.outputs.digest }} @@ -185,8 +183,7 @@ jobs: echo "digest=$DIGEST" >> $GITHUB_OUTPUT - name: Restore test VM images from cache - uses: actions/cache@v4 - id: restore-test-vm-images + uses: actions/cache/restore@v4 with: path: integration/testdata/fixtures/vm-images key: cache-test-vm-images-${{ steps.image-digest.outputs.digest }} From 9d1be410c4ba4150f04dddb85b51aef747e9bf75 Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Mon, 30 Sep 2024 15:22:59 +0400 Subject: [PATCH 006/108] refactor: fix auth error handling (#7615) Signed-off-by: knqyf263 --- internal/dbtest/fake.go | 5 +-- pkg/db/db.go | 43 ++++++++++--------------- pkg/fanal/artifact/image/remote_sbom.go | 5 +-- pkg/javadb/client.go | 7 ++-- pkg/module/command.go | 8 ++--- pkg/oci/artifact.go | 4 +-- pkg/oci/artifact_test.go | 4 +-- pkg/policy/policy.go | 18 +++-------- pkg/policy/policy_test.go | 12 ++----- 9 files changed, 33 insertions(+), 73 deletions(-) diff --git a/internal/dbtest/fake.go b/internal/dbtest/fake.go index 9f2484bbf94a..7943c72273e2 100644 --- a/internal/dbtest/fake.go +++ b/internal/dbtest/fake.go @@ -62,10 +62,7 @@ func NewFakeDB(t *testing.T, dbPath string, opts FakeDBOptions) *oci.Artifact { opt := ftypes.RegistryOptions{ Insecure: false, } - art, err := oci.NewArtifact("dummy", true, opt, oci.WithImage(img)) - require.NoError(t, err) - - return art + return oci.NewArtifact("dummy", true, opt, oci.WithImage(img)) } func ArchiveDir(t *testing.T, dir string) string { diff --git a/pkg/db/db.go b/pkg/db/db.go index e4af60d092c6..344b21b86e44 100644 --- a/pkg/db/db.go +++ b/pkg/db/db.go @@ -153,16 +153,23 @@ func (c *Client) Download(ctx context.Context, dst string, opt types.RegistryOpt log.Debug("No metadata file") } - art, err := c.initOCIArtifact(opt) - if err != nil { - return xerrors.Errorf("OCI artifact error: %w", err) - } - - if err = art.Download(ctx, dst, oci.DownloadOption{MediaType: dbMediaType}); err != nil { + art := c.initOCIArtifact(opt) + if err := art.Download(ctx, dst, oci.DownloadOption{MediaType: dbMediaType}); err != nil { + var terr *transport.Error + if errors.As(err, &terr) { + for _, diagnostic := range terr.Errors { + // For better user experience + if diagnostic.Code == transport.DeniedErrorCode || diagnostic.Code == transport.UnauthorizedErrorCode { + // e.g. https://aquasecurity.github.io/trivy/latest/docs/references/troubleshooting/#db + log.Warnf("See %s", doc.URL("/docs/references/troubleshooting/", "db")) + break + } + } + } return xerrors.Errorf("database download error: %w", err) } - if err = c.updateDownloadedAt(ctx, dst); err != nil { + if err := c.updateDownloadedAt(ctx, dst); err != nil { return xerrors.Errorf("failed to update downloaded_at: %w", err) } return nil @@ -194,27 +201,11 @@ func (c *Client) updateDownloadedAt(ctx context.Context, dbDir string) error { return nil } -func (c *Client) initOCIArtifact(opt types.RegistryOptions) (*oci.Artifact, error) { +func (c *Client) initOCIArtifact(opt types.RegistryOptions) *oci.Artifact { if c.artifact != nil { - return c.artifact, nil - } - - art, err := oci.NewArtifact(c.dbRepository.String(), c.quiet, opt) - if err != nil { - var terr *transport.Error - if errors.As(err, &terr) { - for _, diagnostic := range terr.Errors { - // For better user experience - if diagnostic.Code == transport.DeniedErrorCode || diagnostic.Code == transport.UnauthorizedErrorCode { - // e.g. https://aquasecurity.github.io/trivy/latest/docs/references/troubleshooting/#db - log.Warnf("See %s", doc.URL("/docs/references/troubleshooting/", "db")) - break - } - } - } - return nil, xerrors.Errorf("OCI artifact error: %w", err) + return c.artifact } - return art, nil + return oci.NewArtifact(c.dbRepository.String(), c.quiet, opt) } func (c *Client) ShowInfo() error { diff --git a/pkg/fanal/artifact/image/remote_sbom.go b/pkg/fanal/artifact/image/remote_sbom.go index 37303a9f7b05..e95fef23140c 100644 --- a/pkg/fanal/artifact/image/remote_sbom.go +++ b/pkg/fanal/artifact/image/remote_sbom.go @@ -87,10 +87,6 @@ func (a Artifact) inspectOCIReferrerSBOM(ctx context.Context) (artifact.Referenc func (a Artifact) parseReferrer(ctx context.Context, repo string, desc v1.Descriptor) (artifact.Reference, error) { const fileName string = "referrer.sbom" repoName := fmt.Sprintf("%s@%s", repo, desc.Digest) - referrer, err := oci.NewArtifact(repoName, true, a.artifactOption.ImageOption.RegistryOptions) - if err != nil { - return artifact.Reference{}, xerrors.Errorf("OCI error: %w", err) - } tmpDir, err := os.MkdirTemp("", "trivy-sbom-*") if err != nil { @@ -99,6 +95,7 @@ func (a Artifact) parseReferrer(ctx context.Context, repo string, desc v1.Descri defer os.RemoveAll(tmpDir) // Download SBOM to local filesystem + referrer := oci.NewArtifact(repoName, true, a.artifactOption.ImageOption.RegistryOptions) if err = referrer.Download(ctx, tmpDir, oci.DownloadOption{ MediaType: desc.ArtifactType, Filename: fileName, diff --git a/pkg/javadb/client.go b/pkg/javadb/client.go index 936c2d40c028..9f90a37ba231 100644 --- a/pkg/javadb/client.go +++ b/pkg/javadb/client.go @@ -59,11 +59,8 @@ func (u *Updater) Update() error { log.Info("Downloading the Java DB...") // TODO: support remote options - var a *oci.Artifact - if a, err = oci.NewArtifact(u.repo.String(), u.quiet, u.registryOption); err != nil { - return xerrors.Errorf("oci error: %w", err) - } - if err = a.Download(context.Background(), dbDir, oci.DownloadOption{MediaType: mediaType}); err != nil { + art := oci.NewArtifact(u.repo.String(), u.quiet, u.registryOption) + if err = art.Download(context.Background(), dbDir, oci.DownloadOption{MediaType: mediaType}); err != nil { return xerrors.Errorf("DB download error: %w", err) } diff --git a/pkg/module/command.go b/pkg/module/command.go index 87a87e5d6209..2dd955aa0e4b 100644 --- a/pkg/module/command.go +++ b/pkg/module/command.go @@ -23,15 +23,11 @@ func Install(ctx context.Context, dir, repo string, quiet bool, opt types.Regist } log.Info("Installing the module from the repository...", log.String("repo", repo)) - artifact, err := oci.NewArtifact(repo, quiet, opt) - if err != nil { - return xerrors.Errorf("module initialize error: %w", err) - } + art := oci.NewArtifact(repo, quiet, opt) dst := filepath.Join(dir, ref.Context().Name()) log.Debug("Installing the module...", log.String("dst", dst)) - - if err = artifact.Download(ctx, dst, oci.DownloadOption{MediaType: mediaType}); err != nil { + if err = art.Download(ctx, dst, oci.DownloadOption{MediaType: mediaType}); err != nil { return xerrors.Errorf("module download error: %w", err) } diff --git a/pkg/oci/artifact.go b/pkg/oci/artifact.go index 04d62ee15d36..d8474941b3f5 100644 --- a/pkg/oci/artifact.go +++ b/pkg/oci/artifact.go @@ -57,7 +57,7 @@ type Artifact struct { } // NewArtifact returns a new artifact -func NewArtifact(repo string, quiet bool, registryOpt types.RegistryOptions, opts ...Option) (*Artifact, error) { +func NewArtifact(repo string, quiet bool, registryOpt types.RegistryOptions, opts ...Option) *Artifact { art := &Artifact{ repository: repo, quiet: quiet, @@ -67,7 +67,7 @@ func NewArtifact(repo string, quiet bool, registryOpt types.RegistryOptions, opt for _, o := range opts { o(art) } - return art, nil + return art } func (a *Artifact) populate(ctx context.Context, opt types.RegistryOptions) error { diff --git a/pkg/oci/artifact_test.go b/pkg/oci/artifact_test.go index ddfe0304cc63..7cc8cd19d3c1 100644 --- a/pkg/oci/artifact_test.go +++ b/pkg/oci/artifact_test.go @@ -116,9 +116,7 @@ func TestArtifact_Download(t *testing.T) { }, }, nil) - artifact, err := oci.NewArtifact("repo", true, ftypes.RegistryOptions{}, oci.WithImage(img)) - require.NoError(t, err) - + artifact := oci.NewArtifact("repo", true, ftypes.RegistryOptions{}, oci.WithImage(img)) err = artifact.Download(context.Background(), tempDir, oci.DownloadOption{ MediaType: tt.mediaType, }) diff --git a/pkg/policy/policy.go b/pkg/policy/policy.go index 88af220d3cf6..c8c05cf067c7 100644 --- a/pkg/policy/policy.go +++ b/pkg/policy/policy.go @@ -89,23 +89,16 @@ func NewClient(cacheDir string, quiet bool, checkBundleRepo string, opts ...Opti }, nil } -func (c *Client) populateOCIArtifact(registryOpts types.RegistryOptions) error { +func (c *Client) populateOCIArtifact(registryOpts types.RegistryOptions) { if c.artifact == nil { log.Debug("Loading check bundle", log.String("repository", c.checkBundleRepo)) - art, err := oci.NewArtifact(c.checkBundleRepo, c.quiet, registryOpts) - if err != nil { - return xerrors.Errorf("OCI artifact error: %w", err) - } - c.artifact = art + c.artifact = oci.NewArtifact(c.checkBundleRepo, c.quiet, registryOpts) } - return nil } // DownloadBuiltinPolicies download default policies from GitHub Pages func (c *Client) DownloadBuiltinPolicies(ctx context.Context, registryOpts types.RegistryOptions) error { - if err := c.populateOCIArtifact(registryOpts); err != nil { - return xerrors.Errorf("OPA bundle error: %w", err) - } + c.populateOCIArtifact(registryOpts) dst := c.contentDir() if err := c.artifact.Download(ctx, dst, oci.DownloadOption{MediaType: policyMediaType}); err != nil { @@ -165,10 +158,7 @@ func (c *Client) NeedsUpdate(ctx context.Context, registryOpts types.RegistryOpt return false, nil } - if err = c.populateOCIArtifact(registryOpts); err != nil { - return false, xerrors.Errorf("OPA bundle error: %w", err) - } - + c.populateOCIArtifact(registryOpts) digest, err := c.artifact.Digest(ctx) if err != nil { return false, xerrors.Errorf("digest error: %w", err) diff --git a/pkg/policy/policy_test.go b/pkg/policy/policy_test.go index 8e1b175da483..4dbd6b9a7558 100644 --- a/pkg/policy/policy_test.go +++ b/pkg/policy/policy_test.go @@ -116,9 +116,7 @@ func TestClient_LoadBuiltinPolicies(t *testing.T) { }, nil) // Mock OCI artifact - art, err := oci.NewArtifact("repo", true, ftypes.RegistryOptions{}, oci.WithImage(img)) - require.NoError(t, err) - + art := oci.NewArtifact("repo", true, ftypes.RegistryOptions{}, oci.WithImage(img)) c, err := policy.NewClient(tt.cacheDir, true, "", policy.WithOCIArtifact(art)) require.NoError(t, err) @@ -257,9 +255,7 @@ func TestClient_NeedsUpdate(t *testing.T) { require.NoError(t, err) } - art, err := oci.NewArtifact("repo", true, ftypes.RegistryOptions{}, oci.WithImage(img)) - require.NoError(t, err) - + art := oci.NewArtifact("repo", true, ftypes.RegistryOptions{}, oci.WithImage(img)) c, err := policy.NewClient(tmpDir, true, "", policy.WithOCIArtifact(art), policy.WithClock(tt.clock)) require.NoError(t, err) @@ -361,9 +357,7 @@ func TestClient_DownloadBuiltinPolicies(t *testing.T) { }, nil) // Mock OCI artifact - art, err := oci.NewArtifact("repo", true, ftypes.RegistryOptions{}, oci.WithImage(img)) - require.NoError(t, err) - + art := oci.NewArtifact("repo", true, ftypes.RegistryOptions{}, oci.WithImage(img)) c, err := policy.NewClient(tempDir, true, "", policy.WithClock(tt.clock), policy.WithOCIArtifact(art)) require.NoError(t, err) From 60725f879ba014c5c57583db6afc290b78facae8 Mon Sep 17 00:00:00 2001 From: afdesk Date: Mon, 30 Sep 2024 18:42:46 +0600 Subject: [PATCH 007/108] feat(secret): enhance secret scanning for python binary files (#7223) Signed-off-by: knqyf263 Co-authored-by: knqyf263 --- docs/docs/scanner/secret.md | 4 +- pkg/fanal/analyzer/secret/secret.go | 28 ++++++++++--- pkg/fanal/analyzer/secret/secret_test.go | 25 ++++++++++++ .../secret/testdata/secret.cpython-310.pyc | Bin 0 -> 226 bytes pkg/fanal/secret/scanner.go | 10 ++++- pkg/fanal/utils/utils.go | 38 ++++++++++++++++++ 6 files changed, 96 insertions(+), 9 deletions(-) create mode 100755 pkg/fanal/analyzer/secret/testdata/secret.cpython-310.pyc diff --git a/docs/docs/scanner/secret.md b/docs/docs/scanner/secret.md index 49d8c8488ece..ea74ae64c930 100644 --- a/docs/docs/scanner/secret.md +++ b/docs/docs/scanner/secret.md @@ -3,7 +3,9 @@ Trivy scans any container image, filesystem and git repository to detect exposed secrets like passwords, api keys, and tokens. Secret scanning is enabled by default. -Trivy will scan every plaintext file, according to builtin rules or configuration. There are plenty of builtin rules: +Trivy will scan every plaintext file, according to builtin rules or configuration. Also, Trivy can detect secrets in compiled Python files (`.pyc`). + +There are plenty of builtin rules: - AWS access key - GCP service account diff --git a/pkg/fanal/analyzer/secret/secret.go b/pkg/fanal/analyzer/secret/secret.go index d2627a840c1b..bdb3e96d1428 100644 --- a/pkg/fanal/analyzer/secret/secret.go +++ b/pkg/fanal/analyzer/secret/secret.go @@ -54,6 +54,9 @@ var ( ".gz", ".gzip", ".tar", + } + + allowedBinaries = []string{ ".pyc", } ) @@ -63,6 +66,10 @@ func init() { analyzer.RegisterAnalyzer(NewSecretAnalyzer(secret.Scanner{}, "")) } +func allowedBinary(filename string) bool { + return slices.Contains(allowedBinaries, filepath.Ext(filename)) +} + // SecretAnalyzer is an analyzer for secrets type SecretAnalyzer struct { scanner secret.Scanner @@ -96,7 +103,7 @@ func (a *SecretAnalyzer) Init(opt analyzer.AnalyzerOptions) error { func (a *SecretAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) { // Do not scan binaries binary, err := utils.IsBinary(input.Content, input.Info.Size()) - if binary || err != nil { + if err != nil || (binary && !allowedBinary(input.FilePath)) { return nil, nil } @@ -104,12 +111,20 @@ func (a *SecretAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput log.WithPrefix("secret").Warn("The size of the scanned file is too large. It is recommended to use `--skip-files` for this file to avoid high memory consumption.", log.FilePath(input.FilePath), log.Int64("size (MB)", size/1048576)) } - content, err := io.ReadAll(input.Content) - if err != nil { - return nil, xerrors.Errorf("read error %s: %w", input.FilePath, err) - } + var content []byte - content = bytes.ReplaceAll(content, []byte("\r"), []byte("")) + if !binary { + content, err = io.ReadAll(input.Content) + if err != nil { + return nil, xerrors.Errorf("read error %s: %w", input.FilePath, err) + } + content = bytes.ReplaceAll(content, []byte("\r"), []byte("")) + } else { + content, err = utils.ExtractPrintableBytes(input.Content) + if err != nil { + return nil, xerrors.Errorf("binary read error %s: %w", input.FilePath, err) + } + } filePath := input.FilePath // Files extracted from the image have an empty input.Dir. @@ -122,6 +137,7 @@ func (a *SecretAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput result := a.scanner.Scan(secret.ScanArgs{ FilePath: filePath, Content: content, + Binary: binary, }) if len(result.Findings) == 0 { diff --git a/pkg/fanal/analyzer/secret/secret_test.go b/pkg/fanal/analyzer/secret/secret_test.go index 7cba1d137e8f..5187ebc341bf 100644 --- a/pkg/fanal/analyzer/secret/secret_test.go +++ b/pkg/fanal/analyzer/secret/secret_test.go @@ -95,6 +95,16 @@ func TestSecretAnalyzer(t *testing.T) { }, }, } + wantFindingGH_PAT := types.SecretFinding{ + RuleID: "github-fine-grained-pat", + Category: "GitHub", + Title: "GitHub Fine-grained personal access tokens", + Severity: "CRITICAL", + StartLine: 1, + EndLine: 1, + Match: "Binary file \"/testdata/secret.cpython-310.pyc\" matches a rule \"GitHub Fine-grained personal access tokens\"", + } + tests := []struct { name string configPath string @@ -153,6 +163,21 @@ func TestSecretAnalyzer(t *testing.T) { filePath: "testdata/binaryfile", want: nil, }, + { + name: "python binary file", + configPath: "testdata/skip-tests-config.yaml", + filePath: "testdata/secret.cpython-310.pyc", + want: &analyzer.AnalysisResult{ + Secrets: []types.Secret{ + { + FilePath: "/testdata/secret.cpython-310.pyc", + Findings: []types.SecretFinding{ + wantFindingGH_PAT, + }, + }, + }, + }, + }, } for _, tt := range tests { diff --git a/pkg/fanal/analyzer/secret/testdata/secret.cpython-310.pyc b/pkg/fanal/analyzer/secret/testdata/secret.cpython-310.pyc new file mode 100755 index 0000000000000000000000000000000000000000..2bb659e7eb21abd1fed3da8d1f085600619d6f4b GIT binary patch literal 226 zcmd1j<>g`kf~l#i(<*@UV-N=!FabFZKwK;UBvKfn7*ZKi8JZax8B!R788n%0#inPL zWRxbw7bKR%8yY&fxVreddm0qydZb1gR(g7bC%VKZd6tJ1dsQZeB)a>Ag{D`fx>Xh# zI;KWsS45O#Mmjr(W*3&arFy3MmPa}Hc!rvIxca9?IfeN+R+jkzZD21>O)g3;F}%fE tP?VWhvXY^Q1?Uto@rzqeA0n$)P minLength { + // add a newline between printable lines to separate them + _ = currentPrintableLine.WriteByte('\n') + result = append(result, currentPrintableLine.Bytes()...) + } + currentPrintableLine.Reset() + } + if currentPrintableLine.Len() > minLength { + // add a newline between printable lines to separate them + _ = currentPrintableLine.WriteByte('\n') + result = append(result, currentPrintableLine.Bytes()...) + } + return result, nil +} From b8362321adb2af220830c5de31c29978423d47da Mon Sep 17 00:00:00 2001 From: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> Date: Mon, 30 Sep 2024 20:47:51 +0600 Subject: [PATCH 008/108] feat(java): add empty versions if `pom.xml` dependency versions can't be detected (#7520) Co-authored-by: Teppei Fukuda --- docs/docs/coverage/language/java.md | 11 ++++++ pkg/dependency/parser/java/pom/artifact.go | 21 +++++++++-- pkg/dependency/parser/java/pom/parse.go | 8 +++++ pkg/dependency/parser/java/pom/parse_test.go | 35 +++++++++++++++++++ .../pom/testdata/dep-without-version/pom.xml | 24 +++++++++++++ .../analyzer/language/java/pom/pom_test.go | 11 ++++++ 6 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 pkg/dependency/parser/java/pom/testdata/dep-without-version/pom.xml diff --git a/docs/docs/coverage/language/java.md b/docs/docs/coverage/language/java.md index 67cd8c135b9d..0ee39bbd3155 100644 --- a/docs/docs/coverage/language/java.md +++ b/docs/docs/coverage/language/java.md @@ -69,6 +69,16 @@ The vulnerability database will be downloaded anyway. !!! Warning Trivy may skip some dependencies (that were not found on your local machine) when the `--offline-scan` flag is passed. +### empty dependency version +There are cases when Trivy cannot determine the version of dependencies: + +- Unable to determine the version from the parent because the parent is not reachable; +- The dependency uses a [hard requirement][version-requirement] with more than one version. + +In these cases, Trivy uses an empty version for the dependency. + +!!! Warning + Trivy doesn't detect child dependencies for dependencies without a version. ### maven-invoker-plugin Typically, the integration tests directory (`**/[src|target]/it/*/pom.xml`) of [maven-invoker-plugin][maven-invoker-plugin] doesn't contain actual `pom.xml` files and should be skipped to avoid noise. @@ -120,3 +130,4 @@ Make sure that you have cache[^8] directory to find licenses from `*.pom` depend [maven-pom-repos]: https://maven.apache.org/settings.html#repositories [sbt-dependency-lock]: https://stringbean.github.io/sbt-dependency-lock [detection-priority]: ../../scanner/vulnerability.md#detection-priority +[version-requirement]: https://maven.apache.org/pom.html#dependency-version-requirement-specification diff --git a/pkg/dependency/parser/java/pom/artifact.go b/pkg/dependency/parser/java/pom/artifact.go index b2e97efb229b..73ebe6b21407 100644 --- a/pkg/dependency/parser/java/pom/artifact.go +++ b/pkg/dependency/parser/java/pom/artifact.go @@ -6,15 +6,22 @@ import ( "regexp" "slices" "strings" + "sync" "github.com/samber/lo" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/log" + "github.com/aquasecurity/trivy/pkg/version/doc" ) var ( - varRegexp = regexp.MustCompile(`\${(\S+?)}`) + varRegexp = regexp.MustCompile(`\${(\S+?)}`) + emptyVersionWarn = sync.OnceFunc(func() { + log.WithPrefix("pom").Warn("Dependency version cannot be determined. Child dependencies will not be found.", + // e.g. https://aquasecurity.github.io/trivy/latest/docs/coverage/language/java/#empty-dependency-version + log.String("details", doc.URL("/docs/coverage/language/java/", "empty-dependency-version"))) + }) ) type artifact struct { @@ -42,7 +49,17 @@ func newArtifact(groupID, artifactID, version string, licenses []string, props m } func (a artifact) IsEmpty() bool { - return a.GroupID == "" || a.ArtifactID == "" || a.Version.String() == "" + if a.GroupID == "" || a.ArtifactID == "" { + return true + } + if a.Version.String() == "" { + emptyVersionWarn() + log.WithPrefix("pom").Debug("Dependency version cannot be determined.", + log.String("GroupID", a.GroupID), + log.String("ArtifactID", a.ArtifactID), + ) + } + return false } func (a artifact) Equal(o artifact) bool { diff --git a/pkg/dependency/parser/java/pom/parse.go b/pkg/dependency/parser/java/pom/parse.go index 76ca37f2dd05..542abcac3c8a 100644 --- a/pkg/dependency/parser/java/pom/parse.go +++ b/pkg/dependency/parser/java/pom/parse.go @@ -292,6 +292,14 @@ func (p *Parser) resolve(art artifact, rootDepManagement []pomDependency) (analy return *result, nil } + // We can't resolve a dependency without a version. + // So let's just keep this dependency. + if art.Version.String() == "" { + return analysisResult{ + artifact: art, + }, nil + } + p.logger.Debug("Resolving...", log.String("group_id", art.GroupID), log.String("artifact_id", art.ArtifactID), log.String("version", art.Version.String())) pomContent, err := p.tryRepository(art.GroupID, art.ArtifactID, art.Version.String()) diff --git a/pkg/dependency/parser/java/pom/parse_test.go b/pkg/dependency/parser/java/pom/parse_test.go index 9fa97ffd3672..82604846a4f3 100644 --- a/pkg/dependency/parser/java/pom/parse_test.go +++ b/pkg/dependency/parser/java/pom/parse_test.go @@ -809,6 +809,17 @@ func TestPom_Parse(t *testing.T) { Licenses: []string{"Apache 2.0"}, Relationship: ftypes.RelationshipRoot, }, + { + ID: "org.example:example-api", + Name: "org.example:example-api", + Relationship: ftypes.RelationshipDirect, + Locations: []ftypes.Location{ + { + StartLine: 28, + EndLine: 32, + }, + }, + }, }, }, { @@ -1499,6 +1510,30 @@ func TestPom_Parse(t *testing.T) { }, }, }, + { + name: "dependency without version", + inputFile: filepath.Join("testdata", "dep-without-version", "pom.xml"), + local: true, + want: []ftypes.Package{ + { + ID: "com.example:dep-without-version:1.0.0", + Name: "com.example:dep-without-version", + Version: "1.0.0", + Relationship: ftypes.RelationshipRoot, + }, + { + ID: "org.example:example-api", + Name: "org.example:example-api", + Relationship: ftypes.RelationshipDirect, + Locations: ftypes.Locations{ + { + StartLine: 19, + EndLine: 22, + }, + }, + }, + }, + }, // [INFO] com.example:root-depManagement-in-parent:jar:1.0.0 // [INFO] \- org.example:example-dependency:jar:2.0.0:compile // [INFO] \- org.example:example-api:jar:1.0.1:compile diff --git a/pkg/dependency/parser/java/pom/testdata/dep-without-version/pom.xml b/pkg/dependency/parser/java/pom/testdata/dep-without-version/pom.xml new file mode 100644 index 000000000000..d7da70dd4735 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/dep-without-version/pom.xml @@ -0,0 +1,24 @@ + + 4.0.0 + + no-parent + Parent not found + + + com.example + wrong-parent + 1.0.0 + + + com.example + dep-without-version + 1.0.0 + + + + org.example + example-api + + + diff --git a/pkg/fanal/analyzer/language/java/pom/pom_test.go b/pkg/fanal/analyzer/language/java/pom/pom_test.go index b3aaf43a82cf..ea147a4d1141 100644 --- a/pkg/fanal/analyzer/language/java/pom/pom_test.go +++ b/pkg/fanal/analyzer/language/java/pom/pom_test.go @@ -147,6 +147,17 @@ func Test_pomAnalyzer_Analyze(t *testing.T) { Licenses: []string{"Apache-2.0"}, Relationship: types.RelationshipRoot, }, + { + ID: "org.example:example-api", + Name: "org.example:example-api", + Relationship: types.RelationshipDirect, + Locations: []types.Location{ + { + StartLine: 21, + EndLine: 25, + }, + }, + }, }, }, }, From d4edeb5d62be55e01ab02387dd5f2206ffceb424 Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Tue, 1 Oct 2024 08:27:02 +0400 Subject: [PATCH 009/108] test: use loaded image names (#7617) Signed-off-by: knqyf263 --- integration/docker_engine_test.go | 209 ++++++++------------- internal/testutil/docker.go | 57 ++++++ pkg/fanal/test/integration/library_test.go | 100 ++++------ 3 files changed, 178 insertions(+), 188 deletions(-) create mode 100644 internal/testutil/docker.go diff --git a/integration/docker_engine_test.go b/integration/docker_engine_test.go index 7f9a3119a156..a543d22e466f 100644 --- a/integration/docker_engine_test.go +++ b/integration/docker_engine_test.go @@ -1,19 +1,16 @@ //go:build integration -// +build integration package integration import ( "context" - "io" "os" "strings" "testing" + "github.com/aquasecurity/trivy/internal/testutil" "github.com/aquasecurity/trivy/pkg/types" - "github.com/docker/docker/api/types/image" - "github.com/docker/docker/client" "github.com/stretchr/testify/require" ) @@ -23,7 +20,6 @@ func TestDockerEngine(t *testing.T) { } tests := []struct { name string - imageTag string invalidImage bool ignoreUnfixed bool ignoreStatus []string @@ -34,10 +30,9 @@ func TestDockerEngine(t *testing.T) { wantErr string }{ { - name: "alpine:3.9", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:alpine-39", - input: "testdata/fixtures/images/alpine-39.tar.gz", - golden: "testdata/alpine-39.json.golden", + name: "alpine:3.9", + input: "testdata/fixtures/images/alpine-39.tar.gz", + golden: "testdata/alpine-39.json.golden", }, { name: "alpine:3.9, with high and critical severity", @@ -45,13 +40,11 @@ func TestDockerEngine(t *testing.T) { "HIGH", "CRITICAL", }, - imageTag: "ghcr.io/aquasecurity/trivy-test-images:alpine-39", - input: "testdata/fixtures/images/alpine-39.tar.gz", - golden: "testdata/alpine-39-high-critical.json.golden", + input: "testdata/fixtures/images/alpine-39.tar.gz", + golden: "testdata/alpine-39-high-critical.json.golden", }, { - name: "alpine:3.9, with .trivyignore", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:alpine-39", + name: "alpine:3.9, with .trivyignore", ignoreIDs: []string{ "CVE-2019-1549", "CVE-2019-14697", @@ -60,167 +53,141 @@ func TestDockerEngine(t *testing.T) { golden: "testdata/alpine-39-ignore-cveids.json.golden", }, { - name: "alpine:3.10", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:alpine-310", - input: "testdata/fixtures/images/alpine-310.tar.gz", - golden: "testdata/alpine-310.json.golden", + name: "alpine:3.10", + input: "testdata/fixtures/images/alpine-310.tar.gz", + golden: "testdata/alpine-310.json.golden", }, { - name: "amazonlinux:1", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:amazon-1", - input: "testdata/fixtures/images/amazon-1.tar.gz", - golden: "testdata/amazon-1.json.golden", + name: "amazonlinux:1", + input: "testdata/fixtures/images/amazon-1.tar.gz", + golden: "testdata/amazon-1.json.golden", }, { - name: "amazonlinux:2", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:amazon-2", - input: "testdata/fixtures/images/amazon-2.tar.gz", - golden: "testdata/amazon-2.json.golden", + name: "amazonlinux:2", + input: "testdata/fixtures/images/amazon-2.tar.gz", + golden: "testdata/amazon-2.json.golden", }, { - name: "almalinux 8", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:almalinux-8", - input: "testdata/fixtures/images/almalinux-8.tar.gz", - golden: "testdata/almalinux-8.json.golden", + name: "almalinux 8", + input: "testdata/fixtures/images/almalinux-8.tar.gz", + golden: "testdata/almalinux-8.json.golden", }, { - name: "rocky linux 8", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:rockylinux-8", - input: "testdata/fixtures/images/rockylinux-8.tar.gz", - golden: "testdata/rockylinux-8.json.golden", + name: "rocky linux 8", + input: "testdata/fixtures/images/rockylinux-8.tar.gz", + golden: "testdata/rockylinux-8.json.golden", }, { - name: "centos 6", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:centos-6", - input: "testdata/fixtures/images/centos-6.tar.gz", - golden: "testdata/centos-6.json.golden", + name: "centos 6", + input: "testdata/fixtures/images/centos-6.tar.gz", + golden: "testdata/centos-6.json.golden", }, { - name: "centos 7", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:centos-7", - input: "testdata/fixtures/images/centos-7.tar.gz", - golden: "testdata/centos-7.json.golden", + name: "centos 7", + input: "testdata/fixtures/images/centos-7.tar.gz", + golden: "testdata/centos-7.json.golden", }, { name: "centos 7, with --ignore-unfixed option", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:centos-7", ignoreUnfixed: true, input: "testdata/fixtures/images/centos-7.tar.gz", golden: "testdata/centos-7-ignore-unfixed.json.golden", }, { name: "centos 7, with --ignore-status option", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:centos-7", ignoreStatus: []string{"will_not_fix"}, input: "testdata/fixtures/images/centos-7.tar.gz", golden: "testdata/centos-7-ignore-unfixed.json.golden", }, { name: "centos 7, with --ignore-unfixed option, with medium severity", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:centos-7", ignoreUnfixed: true, severity: []string{"MEDIUM"}, input: "testdata/fixtures/images/centos-7.tar.gz", golden: "testdata/centos-7-medium.json.golden", }, { - name: "registry.redhat.io/ubi7", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:ubi-7", - input: "testdata/fixtures/images/ubi-7.tar.gz", - golden: "testdata/ubi-7.json.golden", + name: "registry.redhat.io/ubi7", + input: "testdata/fixtures/images/ubi-7.tar.gz", + golden: "testdata/ubi-7.json.golden", }, { - name: "debian buster/10", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:debian-buster", - input: "testdata/fixtures/images/debian-buster.tar.gz", - golden: "testdata/debian-buster.json.golden", + name: "debian buster/10", + input: "testdata/fixtures/images/debian-buster.tar.gz", + golden: "testdata/debian-buster.json.golden", }, { name: "debian buster/10, with --ignore-unfixed option", ignoreUnfixed: true, - imageTag: "ghcr.io/aquasecurity/trivy-test-images:debian-buster", input: "testdata/fixtures/images/debian-buster.tar.gz", golden: "testdata/debian-buster-ignore-unfixed.json.golden", }, { name: "debian buster/10, with --ignore-status option", ignoreStatus: []string{"affected"}, - imageTag: "ghcr.io/aquasecurity/trivy-test-images:debian-buster", input: "testdata/fixtures/images/debian-buster.tar.gz", golden: "testdata/debian-buster-ignore-unfixed.json.golden", }, { - name: "debian stretch/9", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:debian-stretch", - input: "testdata/fixtures/images/debian-stretch.tar.gz", - golden: "testdata/debian-stretch.json.golden", + name: "debian stretch/9", + input: "testdata/fixtures/images/debian-stretch.tar.gz", + golden: "testdata/debian-stretch.json.golden", }, { - name: "distroless base", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:distroless-base", - input: "testdata/fixtures/images/distroless-base.tar.gz", - golden: "testdata/distroless-base.json.golden", + name: "distroless base", + input: "testdata/fixtures/images/distroless-base.tar.gz", + golden: "testdata/distroless-base.json.golden", }, { - name: "distroless python2.7", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:distroless-python27", - input: "testdata/fixtures/images/distroless-python27.tar.gz", - golden: "testdata/distroless-python27.json.golden", + name: "distroless python2.7", + input: "testdata/fixtures/images/distroless-python27.tar.gz", + golden: "testdata/distroless-python27.json.golden", }, { - name: "oracle linux 8", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:oraclelinux-8", - input: "testdata/fixtures/images/oraclelinux-8.tar.gz", - golden: "testdata/oraclelinux-8.json.golden", + name: "oracle linux 8", + input: "testdata/fixtures/images/oraclelinux-8.tar.gz", + golden: "testdata/oraclelinux-8.json.golden", }, { - name: "ubuntu 18.04", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:ubuntu-1804", - input: "testdata/fixtures/images/ubuntu-1804.tar.gz", - golden: "testdata/ubuntu-1804.json.golden", + name: "ubuntu 18.04", + input: "testdata/fixtures/images/ubuntu-1804.tar.gz", + golden: "testdata/ubuntu-1804.json.golden", }, { name: "ubuntu 18.04, with --ignore-unfixed option", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:ubuntu-1804", ignoreUnfixed: true, input: "testdata/fixtures/images/ubuntu-1804.tar.gz", golden: "testdata/ubuntu-1804-ignore-unfixed.json.golden", }, { - name: "opensuse leap 15.1", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:opensuse-leap-151", - input: "testdata/fixtures/images/opensuse-leap-151.tar.gz", - golden: "testdata/opensuse-leap-151.json.golden", + name: "opensuse leap 15.1", + input: "testdata/fixtures/images/opensuse-leap-151.tar.gz", + golden: "testdata/opensuse-leap-151.json.golden", }, { - name: "opensuse tumbleweed", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:opensuse-tumbleweed", - input: "testdata/fixtures/images/opensuse-tumbleweed.tar.gz", - golden: "testdata/opensuse-tumbleweed.json.golden", + name: "opensuse tumbleweed", + input: "testdata/fixtures/images/opensuse-tumbleweed.tar.gz", + golden: "testdata/opensuse-tumbleweed.json.golden", }, { - name: "sle micro rancher 5.4", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:sle-micro-rancher-5.4_ndb", - input: "testdata/fixtures/images/sle-micro-rancher-5.4_ndb.tar.gz", - golden: "testdata/sl-micro-rancher5.4.json.golden", + name: "sle micro rancher 5.4", + input: "testdata/fixtures/images/sle-micro-rancher-5.4_ndb.tar.gz", + golden: "testdata/sl-micro-rancher5.4.json.golden", }, { - name: "photon 3.0", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:photon-30", - input: "testdata/fixtures/images/photon-30.tar.gz", - golden: "testdata/photon-30.json.golden", + name: "photon 3.0", + input: "testdata/fixtures/images/photon-30.tar.gz", + golden: "testdata/photon-30.json.golden", }, { - name: "CBL-Mariner 1.0", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:mariner-1.0", - input: "testdata/fixtures/images/mariner-1.0.tar.gz", - golden: "testdata/mariner-1.0.json.golden", + name: "CBL-Mariner 1.0", + input: "testdata/fixtures/images/mariner-1.0.tar.gz", + golden: "testdata/mariner-1.0.json.golden", }, { - name: "busybox with Cargo.lock", - imageTag: "ghcr.io/aquasecurity/trivy-test-images:busybox-with-lockfile", - input: "testdata/fixtures/images/busybox-with-lockfile.tar.gz", - golden: "testdata/busybox-with-lockfile.json.golden", + name: "busybox with Cargo.lock", + input: "testdata/fixtures/images/busybox-with-lockfile.tar.gz", + golden: "testdata/busybox-with-lockfile.json.golden", }, { name: "sad path, invalid image", @@ -239,44 +206,27 @@ func TestDockerEngine(t *testing.T) { ctx := context.Background() defer ctx.Done() - cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) - require.NoError(t, err) + cli := testutil.NewDockerClient(t) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if !tt.invalidImage { testfile, err := os.Open(tt.input) require.NoError(t, err, tt.name) + defer testfile.Close() - // ensure image doesnt already exists - _, _ = cli.ImageRemove(ctx, tt.input, image.RemoveOptions{ - Force: true, - PruneChildren: true, - }) + // Ensure image doesn't already exist + cli.ImageRemove(t, ctx, tt.input) - // load image into docker engine - res, err := cli.ImageLoad(ctx, testfile, true) - require.NoError(t, err, tt.name) - if _, err := io.Copy(io.Discard, res.Body); err != nil { - require.NoError(t, err, tt.name) - } - defer res.Body.Close() + // Load image into docker engine + loadedImage := cli.ImageLoad(t, ctx, tt.input) - // tag our image to something unique - err = cli.ImageTag(ctx, tt.imageTag, tt.input) + // Tag our image to something unique + err = cli.ImageTag(ctx, loadedImage, tt.input) require.NoError(t, err, tt.name) - // cleanup - t.Cleanup(func() { - _, _ = cli.ImageRemove(ctx, tt.input, image.RemoveOptions{ - Force: true, - PruneChildren: true, - }) - _, _ = cli.ImageRemove(ctx, tt.imageTag, image.RemoveOptions{ - Force: true, - PruneChildren: true, - }) - }) + // Cleanup + t.Cleanup(func() { cli.ImageRemove(t, ctx, tt.input) }) } osArgs := []string{ @@ -309,7 +259,7 @@ func TestDockerEngine(t *testing.T) { } if len(tt.ignoreIDs) != 0 { trivyIgnore := ".trivyignore" - err = os.WriteFile(trivyIgnore, []byte(strings.Join(tt.ignoreIDs, "\n")), 0444) + err := os.WriteFile(trivyIgnore, []byte(strings.Join(tt.ignoreIDs, "\n")), 0444) require.NoError(t, err, "failed to write .trivyignore") defer os.Remove(trivyIgnore) } @@ -320,7 +270,8 @@ func TestDockerEngine(t *testing.T) { wantErr: tt.wantErr, // Container field was removed in Docker Engine v26.0 // cf. https://github.com/docker/cli/blob/v26.1.3/docs/deprecated.md#container-and-containerconfig-fields-in-image-inspect - override: overrideFuncs(overrideUID, func(t *testing.T, want, _ *types.Report) { + override: overrideFuncs(overrideUID, func(t *testing.T, want, got *types.Report) { + got.Metadata.ImageConfig.Container = "" want.Metadata.ImageConfig.Container = "" }), }) diff --git a/internal/testutil/docker.go b/internal/testutil/docker.go new file mode 100644 index 000000000000..60e1bb3ab668 --- /dev/null +++ b/internal/testutil/docker.go @@ -0,0 +1,57 @@ +package testutil + +import ( + "context" + "encoding/json" + "os" + "strings" + "testing" + + "github.com/docker/docker/api/types/image" + "github.com/docker/docker/client" + "github.com/stretchr/testify/require" +) + +type DockerClient struct { + *client.Client +} + +func NewDockerClient(t *testing.T) *DockerClient { + cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + require.NoError(t, err) + return &DockerClient{Client: cli} +} + +func (c *DockerClient) ImageLoad(t *testing.T, ctx context.Context, imageFile string) string { + t.Helper() + testfile, err := os.Open(imageFile) + require.NoError(t, err) + defer testfile.Close() + + // Load image into docker engine + res, err := c.Client.ImageLoad(ctx, testfile, true) + require.NoError(t, err) + defer res.Body.Close() + + // Parse the response and extract the loaded image name + var data struct { + Stream string `json:"stream"` + } + err = json.NewDecoder(res.Body).Decode(&data) + require.NoError(t, err) + loadedImage := strings.TrimPrefix(data.Stream, "Loaded image: ") + loadedImage = strings.TrimSpace(loadedImage) + require.NotEmpty(t, loadedImage, data.Stream) + + t.Cleanup(func() { c.ImageRemove(t, ctx, loadedImage) }) + + return loadedImage +} + +func (c *DockerClient) ImageRemove(t *testing.T, ctx context.Context, imageID string) { + t.Helper() + _, _ = c.Client.ImageRemove(ctx, imageID, image.RemoveOptions{ + Force: true, + PruneChildren: true, + }) +} diff --git a/pkg/fanal/test/integration/library_test.go b/pkg/fanal/test/integration/library_test.go index ea76c7965b3a..ebe17d6e2188 100644 --- a/pkg/fanal/test/integration/library_test.go +++ b/pkg/fanal/test/integration/library_test.go @@ -7,20 +7,13 @@ import ( "encoding/json" "flag" "fmt" - "io" "os" "sort" - "strings" "testing" - dimage "github.com/docker/docker/api/types/image" - "github.com/docker/docker/client" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/pkg/fanal/analyzer" - + "github.com/aquasecurity/trivy/internal/testutil" "github.com/aquasecurity/trivy/pkg/cache" + "github.com/aquasecurity/trivy/pkg/fanal/analyzer" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/all" "github.com/aquasecurity/trivy/pkg/fanal/applier" "github.com/aquasecurity/trivy/pkg/fanal/artifact" @@ -29,6 +22,9 @@ import ( "github.com/aquasecurity/trivy/pkg/fanal/image" "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + _ "modernc.org/sqlite" ) @@ -36,7 +32,7 @@ var update = flag.Bool("update", false, "update golden files") type testCase struct { name string - remoteImageName string + imageTag string imageFile string wantOS types.OS wantPkgsFromCmds string @@ -45,63 +41,63 @@ type testCase struct { var tests = []testCase{ { - name: "happy path, alpine:3.10", - remoteImageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310", - imageFile: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz", + name: "happy path, alpine:3.10", + imageTag: "alpine-310", + imageFile: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz", wantOS: types.OS{ Name: "3.10.2", Family: "alpine", }, }, { - name: "happy path, amazonlinux:2", - remoteImageName: "ghcr.io/aquasecurity/trivy-test-images:amazon-2", - imageFile: "../../../../integration/testdata/fixtures/images/amazon-2.tar.gz", + name: "happy path, amazonlinux:2", + imageTag: "amazon-2", + imageFile: "../../../../integration/testdata/fixtures/images/amazon-2.tar.gz", wantOS: types.OS{ Name: "2 (Karoo)", Family: "amazon", }, }, { - name: "happy path, debian:buster", - remoteImageName: "ghcr.io/aquasecurity/trivy-test-images:debian-buster", - imageFile: "../../../../integration/testdata/fixtures/images/debian-buster.tar.gz", + name: "happy path, debian:buster", + imageTag: "debian-buster", + imageFile: "../../../../integration/testdata/fixtures/images/debian-buster.tar.gz", wantOS: types.OS{ Name: "10.1", Family: "debian", }, }, { - name: "happy path, photon:3.0", - remoteImageName: "ghcr.io/aquasecurity/trivy-test-images:photon-30", - imageFile: "../../../../integration/testdata/fixtures/images/photon-30.tar.gz", + name: "happy path, photon:3.0", + imageTag: "photon-30", + imageFile: "../../../../integration/testdata/fixtures/images/photon-30.tar.gz", wantOS: types.OS{ Name: "3.0", Family: "photon", }, }, { - name: "happy path, registry.redhat.io/ubi7", - remoteImageName: "ghcr.io/aquasecurity/trivy-test-images:ubi-7", - imageFile: "../../../../integration/testdata/fixtures/images/ubi-7.tar.gz", + name: "happy path, registry.redhat.io/ubi7", + imageTag: "ubi-7", + imageFile: "../../../../integration/testdata/fixtures/images/ubi-7.tar.gz", wantOS: types.OS{ Name: "7.7", Family: "redhat", }, }, { - name: "happy path, opensuse leap 15.1", - remoteImageName: "ghcr.io/aquasecurity/trivy-test-images:opensuse-leap-151", - imageFile: "../../../../integration/testdata/fixtures/images/opensuse-leap-151.tar.gz", + name: "happy path, opensuse leap 15.1", + imageTag: "opensuse-leap-151", + imageFile: "../../../../integration/testdata/fixtures/images/opensuse-leap-151.tar.gz", wantOS: types.OS{ Name: "15.1", Family: "opensuse.leap", }, }, { - name: "happy path, opensuse tumbleweed", - remoteImageName: "ghcr.io/aquasecurity/trivy-test-images:opensuse-tumbleweed", - imageFile: "../../../../integration/testdata/fixtures/images/opensuse-tumbleweed.tar.gz", + name: "happy path, opensuse tumbleweed", + imageTag: "opensuse-tumbleweed", + imageFile: "../../../../integration/testdata/fixtures/images/opensuse-tumbleweed.tar.gz", wantOS: types.OS{ Name: "20240607", Family: "opensuse.tumbleweed", @@ -109,27 +105,27 @@ var tests = []testCase{ }, { // from registry.suse.com/suse/sle15:15.3.17.8.16 - name: "happy path, suse 15.3 (NDB)", - remoteImageName: "ghcr.io/aquasecurity/trivy-test-images:suse-15.3_ndb", - imageFile: "../../../../integration/testdata/fixtures/images/suse-15.3_ndb.tar.gz", + name: "happy path, suse 15.3 (NDB)", + imageTag: "suse-15.3_ndb", + imageFile: "../../../../integration/testdata/fixtures/images/suse-15.3_ndb.tar.gz", wantOS: types.OS{ Name: "15.3", Family: "suse linux enterprise server", }, }, { - name: "happy path, Fedora 35", - remoteImageName: "ghcr.io/aquasecurity/trivy-test-images:fedora-35", - imageFile: "../../../../integration/testdata/fixtures/images/fedora-35.tar.gz", + name: "happy path, Fedora 35", + imageTag: "fedora-35", + imageFile: "../../../../integration/testdata/fixtures/images/fedora-35.tar.gz", wantOS: types.OS{ Name: "35", Family: "fedora", }, }, { - name: "happy path, vulnimage with lock files", - remoteImageName: "ghcr.io/aquasecurity/trivy-test-images:vulnimage", - imageFile: "../../../../integration/testdata/fixtures/images/vulnimage.tar.gz", + name: "happy path, vulnimage with lock files", + imageTag: "vulnimage", + imageFile: "../../../../integration/testdata/fixtures/images/vulnimage.tar.gz", wantOS: types.OS{ Name: "3.7.1", Family: "alpine", @@ -145,6 +141,8 @@ func TestFanal_Library_DockerMode(t *testing.T) { if *update { t.Skipf("This test creates wrong golden file") } + + cli := testutil.NewDockerClient(t) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := context.Background() @@ -153,20 +151,10 @@ func TestFanal_Library_DockerMode(t *testing.T) { c, err := cache.NewFSCache(d) require.NoError(t, err) - cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) - require.NoError(t, err, tt.name) - - testfile, err := os.Open(tt.imageFile) - require.NoError(t, err) - - // load image into docker engine - resp, err := cli.ImageLoad(ctx, testfile, true) - require.NoError(t, err, tt.name) - _, err = io.Copy(io.Discard, resp.Body) - require.NoError(t, err, tt.name) + loadedImage := cli.ImageLoad(t, ctx, tt.imageFile) // Enable only dockerd scanning - img, cleanup, err := image.NewContainerImage(ctx, tt.remoteImageName, types.ImageOptions{ + img, cleanup, err := image.NewContainerImage(ctx, loadedImage, types.ImageOptions{ ImageSources: types.ImageSources{types.DockerImageSource}, }) require.NoError(t, err, tt.name) @@ -190,11 +178,6 @@ func TestFanal_Library_DockerMode(t *testing.T) { // clear Cache require.NoError(t, c.Clear(), tt.name) - - _, _ = cli.ImageRemove(ctx, tt.remoteImageName, dimage.RemoveOptions{ - Force: true, - PruneChildren: true, - }) }) } } @@ -249,8 +232,7 @@ func checkOSPackages(t *testing.T, detail types.ArtifactDetail, tc testCase) { // Sort OS packages for consistency sort.Sort(detail.Packages) - splitted := strings.Split(tc.remoteImageName, ":") - goldenFile := fmt.Sprintf("testdata/goldens/packages/%s.json.golden", splitted[len(splitted)-1]) + goldenFile := fmt.Sprintf("testdata/goldens/packages/%s.json.golden", tc.imageTag) if *update { b, err := json.MarshalIndent(detail.Packages, "", " ") From 7602d14654c2a4cca445439da7b93bfab4b6d872 Mon Sep 17 00:00:00 2001 From: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> Date: Tue, 1 Oct 2024 13:40:20 +0600 Subject: [PATCH 010/108] ci: don't use cache for `setup-go` (#7622) --- .github/workflows/auto-update-labels.yaml | 1 + .github/workflows/cache-test-images.yaml | 2 ++ .github/workflows/test.yaml | 8 ++++++++ 3 files changed, 11 insertions(+) diff --git a/.github/workflows/auto-update-labels.yaml b/.github/workflows/auto-update-labels.yaml index 941e7b5db9df..0de6e41438b9 100644 --- a/.github/workflows/auto-update-labels.yaml +++ b/.github/workflows/auto-update-labels.yaml @@ -20,6 +20,7 @@ jobs: with: # cf. https://github.com/aquasecurity/trivy/pull/6711 go-version: ${{ env.GO_VERSION }} + cache: false - name: Install aqua tools uses: aquaproj/aqua-installer@v3.0.1 diff --git a/.github/workflows/cache-test-images.yaml b/.github/workflows/cache-test-images.yaml index da8aff10cfd2..a03dc683e3ae 100644 --- a/.github/workflows/cache-test-images.yaml +++ b/.github/workflows/cache-test-images.yaml @@ -16,6 +16,7 @@ jobs: uses: actions/setup-go@v5 with: go-version-file: go.mod + cache: false - name: Install tools uses: aquaproj/aqua-installer@v3.0.1 @@ -55,6 +56,7 @@ jobs: uses: actions/setup-go@v5 with: go-version-file: go.mod + cache: false - name: Install tools uses: aquaproj/aqua-installer@v3.0.1 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index b423b61699fb..74b0933c6ef6 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -26,6 +26,8 @@ jobs: uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} + cache: false + - name: go mod tidy run: | go mod tidy @@ -78,6 +80,7 @@ jobs: uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} + cache: false - name: Install tools uses: aquaproj/aqua-installer@v3.0.1 @@ -113,6 +116,7 @@ jobs: uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} + cache: false - name: Install tools uses: aquaproj/aqua-installer@v3.0.1 @@ -133,6 +137,7 @@ jobs: uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} + cache: false - name: Install tools uses: aquaproj/aqua-installer@v3.0.1 @@ -170,6 +175,8 @@ jobs: uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} + cache: false + - name: Install tools uses: aquaproj/aqua-installer@v3.0.1 with: @@ -210,6 +217,7 @@ jobs: uses: actions/setup-go@v5 with: go-version: ${{ env.GO_VERSION }} + cache: false - name: Determine GoReleaser ID id: goreleaser_id From 3562529ddfb26d301311ed450c192e17011353df Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Tue, 1 Oct 2024 19:16:06 +0600 Subject: [PATCH 011/108] feat: support multiple DB repositories for vulnerability and Java DB (#7605) Signed-off-by: nikpivkin --- .../configuration/cli/trivy_filesystem.md | 4 +- .../configuration/cli/trivy_image.md | 4 +- .../configuration/cli/trivy_kubernetes.md | 4 +- .../configuration/cli/trivy_repository.md | 4 +- .../configuration/cli/trivy_rootfs.md | 4 +- .../configuration/cli/trivy_sbom.md | 96 +++++++++---------- .../configuration/cli/trivy_server.md | 2 +- .../references/configuration/cli/trivy_vm.md | 4 +- .../references/configuration/config-file.md | 6 +- internal/dbtest/fake.go | 2 +- pkg/commands/app.go | 2 +- pkg/commands/artifact/run.go | 4 +- pkg/commands/operation/operation.go | 5 +- pkg/commands/server/run.go | 4 +- pkg/db/db.go | 52 +++++----- pkg/db/db_test.go | 2 +- pkg/fanal/analyzer/analyzer_test.go | 4 +- .../analyzer/language/java/jar/jar_test.go | 4 +- pkg/fanal/artifact/image/remote_sbom.go | 3 +- pkg/flag/db_flags.go | 80 +++++++++------- pkg/flag/db_flags_test.go | 72 +++++++++----- pkg/javadb/client.go | 34 ++++--- pkg/module/command.go | 4 +- pkg/oci/artifact.go | 67 +++++++++++-- pkg/oci/artifact_test.go | 3 +- pkg/policy/policy.go | 6 +- pkg/policy/policy_test.go | 6 +- pkg/rpc/server/listen.go | 22 ++--- 28 files changed, 301 insertions(+), 203 deletions(-) diff --git a/docs/docs/references/configuration/cli/trivy_filesystem.md b/docs/docs/references/configuration/cli/trivy_filesystem.md index e907c5d4f9d5..9944a2d618ff 100644 --- a/docs/docs/references/configuration/cli/trivy_filesystem.md +++ b/docs/docs/references/configuration/cli/trivy_filesystem.md @@ -29,7 +29,7 @@ trivy filesystem [flags] PATH --config-data strings specify paths from which data for the Rego checks will be recursively loaded --config-file-schemas strings specify paths to JSON configuration file schemas to determine that a file matches some configuration and pass the schema to Rego checks for type checking --custom-headers strings custom headers in client mode - --db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2") + --db-repository strings OCI repository(ies) to retrieve trivy-db in order of priority (default [ghcr.io/aquasecurity/trivy-db:2]) --dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages --detection-priority string specify the detection priority: - "precise": Prioritizes precise by minimizing false positives. @@ -56,7 +56,7 @@ trivy filesystem [flags] PATH --include-deprecated-checks include deprecated checks (default true) --include-dev-deps include development dependencies in the report (supported: npm, yarn) --include-non-failures include successes and exceptions, available with '--scanners misconfig' - --java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db:1") + --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) --license-confidence-level float specify license classifier's confidence level (default 0.9) --license-full eagerly look for licenses in source code headers and license files --list-all-pkgs output all packages in the JSON report regardless of vulnerability diff --git a/docs/docs/references/configuration/cli/trivy_image.md b/docs/docs/references/configuration/cli/trivy_image.md index 156582f047ad..b63daffbf4ad 100644 --- a/docs/docs/references/configuration/cli/trivy_image.md +++ b/docs/docs/references/configuration/cli/trivy_image.md @@ -43,7 +43,7 @@ trivy image [flags] IMAGE_NAME --config-data strings specify paths from which data for the Rego checks will be recursively loaded --config-file-schemas strings specify paths to JSON configuration file schemas to determine that a file matches some configuration and pass the schema to Rego checks for type checking --custom-headers strings custom headers in client mode - --db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2") + --db-repository strings OCI repository(ies) to retrieve trivy-db in order of priority (default [ghcr.io/aquasecurity/trivy-db:2]) --dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages --detection-priority string specify the detection priority: - "precise": Prioritizes precise by minimizing false positives. @@ -74,7 +74,7 @@ trivy image [flags] IMAGE_NAME --include-deprecated-checks include deprecated checks (default true) --include-non-failures include successes and exceptions, available with '--scanners misconfig' --input string input file path instead of image name - --java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db:1") + --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) --license-confidence-level float specify license classifier's confidence level (default 0.9) --license-full eagerly look for licenses in source code headers and license files --list-all-pkgs output all packages in the JSON report regardless of vulnerability diff --git a/docs/docs/references/configuration/cli/trivy_kubernetes.md b/docs/docs/references/configuration/cli/trivy_kubernetes.md index b7c626a7d476..35190b1b82b5 100644 --- a/docs/docs/references/configuration/cli/trivy_kubernetes.md +++ b/docs/docs/references/configuration/cli/trivy_kubernetes.md @@ -38,7 +38,7 @@ trivy kubernetes [flags] [CONTEXT] --config-check strings specify the paths to the Rego check files or to the directories containing them, applying config files --config-data strings specify paths from which data for the Rego checks will be recursively loaded --config-file-schemas strings specify paths to JSON configuration file schemas to determine that a file matches some configuration and pass the schema to Rego checks for type checking - --db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2") + --db-repository strings OCI repository(ies) to retrieve trivy-db in order of priority (default [ghcr.io/aquasecurity/trivy-db:2]) --dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages --detection-priority string specify the detection priority: - "precise": Prioritizes precise by minimizing false positives. @@ -70,7 +70,7 @@ trivy kubernetes [flags] [CONTEXT] --include-kinds strings indicate the kinds included in scanning (example: node) --include-namespaces strings indicate the namespaces included in scanning (example: kube-system) --include-non-failures include successes and exceptions, available with '--scanners misconfig' - --java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db:1") + --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) --k8s-version string specify k8s version to validate outdated api by it (example: 1.21.0) --kubeconfig string specify the kubeconfig file path to use --list-all-pkgs output all packages in the JSON report regardless of vulnerability diff --git a/docs/docs/references/configuration/cli/trivy_repository.md b/docs/docs/references/configuration/cli/trivy_repository.md index 04f3f26e919a..59e4f2c08cb0 100644 --- a/docs/docs/references/configuration/cli/trivy_repository.md +++ b/docs/docs/references/configuration/cli/trivy_repository.md @@ -29,7 +29,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL) --config-data strings specify paths from which data for the Rego checks will be recursively loaded --config-file-schemas strings specify paths to JSON configuration file schemas to determine that a file matches some configuration and pass the schema to Rego checks for type checking --custom-headers strings custom headers in client mode - --db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2") + --db-repository strings OCI repository(ies) to retrieve trivy-db in order of priority (default [ghcr.io/aquasecurity/trivy-db:2]) --dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages --detection-priority string specify the detection priority: - "precise": Prioritizes precise by minimizing false positives. @@ -56,7 +56,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL) --include-deprecated-checks include deprecated checks (default true) --include-dev-deps include development dependencies in the report (supported: npm, yarn) --include-non-failures include successes and exceptions, available with '--scanners misconfig' - --java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db:1") + --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) --license-confidence-level float specify license classifier's confidence level (default 0.9) --license-full eagerly look for licenses in source code headers and license files --list-all-pkgs output all packages in the JSON report regardless of vulnerability diff --git a/docs/docs/references/configuration/cli/trivy_rootfs.md b/docs/docs/references/configuration/cli/trivy_rootfs.md index 0e0dfa54d448..6022f4589446 100644 --- a/docs/docs/references/configuration/cli/trivy_rootfs.md +++ b/docs/docs/references/configuration/cli/trivy_rootfs.md @@ -31,7 +31,7 @@ trivy rootfs [flags] ROOTDIR --config-data strings specify paths from which data for the Rego checks will be recursively loaded --config-file-schemas strings specify paths to JSON configuration file schemas to determine that a file matches some configuration and pass the schema to Rego checks for type checking --custom-headers strings custom headers in client mode - --db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2") + --db-repository strings OCI repository(ies) to retrieve trivy-db in order of priority (default [ghcr.io/aquasecurity/trivy-db:2]) --dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages --detection-priority string specify the detection priority: - "precise": Prioritizes precise by minimizing false positives. @@ -58,7 +58,7 @@ trivy rootfs [flags] ROOTDIR --ignorefile string specify .trivyignore file (default ".trivyignore") --include-deprecated-checks include deprecated checks (default true) --include-non-failures include successes and exceptions, available with '--scanners misconfig' - --java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db:1") + --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) --license-confidence-level float specify license classifier's confidence level (default 0.9) --license-full eagerly look for licenses in source code headers and license files --list-all-pkgs output all packages in the JSON report regardless of vulnerability diff --git a/docs/docs/references/configuration/cli/trivy_sbom.md b/docs/docs/references/configuration/cli/trivy_sbom.md index 1b0df922c7fc..f09453845518 100644 --- a/docs/docs/references/configuration/cli/trivy_sbom.md +++ b/docs/docs/references/configuration/cli/trivy_sbom.md @@ -20,54 +20,54 @@ trivy sbom [flags] SBOM_PATH ### Options ``` - --cache-backend string [EXPERIMENTAL] cache backend (e.g. redis://localhost:6379) (default "memory") - --cache-ttl duration cache TTL when using redis as cache backend - --compliance string compliance report to generate - --custom-headers strings custom headers in client mode - --db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2") - --detection-priority string specify the detection priority: - - "precise": Prioritizes precise by minimizing false positives. - - "comprehensive": Aims to detect more security findings at the cost of potential false positives. - (precise,comprehensive) (default "precise") - --download-db-only download/update vulnerability database but don't run a scan - --download-java-db-only download/update Java index database but don't run a scan - --exit-code int specify exit code when any security issues are found - --exit-on-eol int exit with the specified code when the OS reaches end of service/life - --file-patterns strings specify config file patterns - -f, --format string format (table,json,template,sarif,cyclonedx,spdx,spdx-json,github,cosign-vuln) (default "table") - -h, --help help for sbom - --ignore-policy string specify the Rego file path to evaluate each vulnerability - --ignore-status strings comma-separated list of vulnerability status to ignore (unknown,not_affected,affected,fixed,under_investigation,will_not_fix,fix_deferred,end_of_life) - --ignore-unfixed display only fixed vulnerabilities - --ignored-licenses strings specify a list of license to ignore - --ignorefile string specify .trivyignore file (default ".trivyignore") - --java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db:1") - --list-all-pkgs output all packages in the JSON report regardless of vulnerability - --no-progress suppress progress bar - --offline-scan do not issue API requests to identify dependencies - -o, --output string output file name - --output-plugin-arg string [EXPERIMENTAL] output plugin arguments - --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect]) - --pkg-types strings list of package types (os,library) (default [os,library]) - --redis-ca string redis ca file location, if using redis as cache backend - --redis-cert string redis certificate file location, if using redis as cache backend - --redis-key string redis key file location, if using redis as cache backend - --redis-tls enable redis TLS with public certificates, if using redis as cache backend - --rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev") - --sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor) - --scanners strings comma-separated list of what security issues to detect (vuln,license) (default [vuln]) - --server string server address in client mode - -s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL]) - --show-suppressed [EXPERIMENTAL] show suppressed vulnerabilities - --skip-db-update skip updating vulnerability database - --skip-dirs strings specify the directories or glob patterns to skip - --skip-files strings specify the files or glob patterns to skip - --skip-java-db-update skip updating Java index database - --skip-vex-repo-update [EXPERIMENTAL] Skip VEX Repository update - -t, --template string output template - --token string for authentication in client/server mode - --token-header string specify a header name for token in client/server mode (default "Trivy-Token") - --vex strings [EXPERIMENTAL] VEX sources ("repo", "oci" or file path) + --cache-backend string [EXPERIMENTAL] cache backend (e.g. redis://localhost:6379) (default "memory") + --cache-ttl duration cache TTL when using redis as cache backend + --compliance string compliance report to generate + --custom-headers strings custom headers in client mode + --db-repository strings OCI repository(ies) to retrieve trivy-db in order of priority (default [ghcr.io/aquasecurity/trivy-db:2]) + --detection-priority string specify the detection priority: + - "precise": Prioritizes precise by minimizing false positives. + - "comprehensive": Aims to detect more security findings at the cost of potential false positives. + (precise,comprehensive) (default "precise") + --download-db-only download/update vulnerability database but don't run a scan + --download-java-db-only download/update Java index database but don't run a scan + --exit-code int specify exit code when any security issues are found + --exit-on-eol int exit with the specified code when the OS reaches end of service/life + --file-patterns strings specify config file patterns + -f, --format string format (table,json,template,sarif,cyclonedx,spdx,spdx-json,github,cosign-vuln) (default "table") + -h, --help help for sbom + --ignore-policy string specify the Rego file path to evaluate each vulnerability + --ignore-status strings comma-separated list of vulnerability status to ignore (unknown,not_affected,affected,fixed,under_investigation,will_not_fix,fix_deferred,end_of_life) + --ignore-unfixed display only fixed vulnerabilities + --ignored-licenses strings specify a list of license to ignore + --ignorefile string specify .trivyignore file (default ".trivyignore") + --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) + --list-all-pkgs output all packages in the JSON report regardless of vulnerability + --no-progress suppress progress bar + --offline-scan do not issue API requests to identify dependencies + -o, --output string output file name + --output-plugin-arg string [EXPERIMENTAL] output plugin arguments + --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect]) + --pkg-types strings list of package types (os,library) (default [os,library]) + --redis-ca string redis ca file location, if using redis as cache backend + --redis-cert string redis certificate file location, if using redis as cache backend + --redis-key string redis key file location, if using redis as cache backend + --redis-tls enable redis TLS with public certificates, if using redis as cache backend + --rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev") + --sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor) + --scanners strings comma-separated list of what security issues to detect (vuln,license) (default [vuln]) + --server string server address in client mode + -s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL]) + --show-suppressed [EXPERIMENTAL] show suppressed vulnerabilities + --skip-db-update skip updating vulnerability database + --skip-dirs strings specify the directories or glob patterns to skip + --skip-files strings specify the files or glob patterns to skip + --skip-java-db-update skip updating Java index database + --skip-vex-repo-update [EXPERIMENTAL] Skip VEX Repository update + -t, --template string output template + --token string for authentication in client/server mode + --token-header string specify a header name for token in client/server mode (default "Trivy-Token") + --vex strings [EXPERIMENTAL] VEX sources ("repo", "oci" or file path) ``` ### Options inherited from parent commands diff --git a/docs/docs/references/configuration/cli/trivy_server.md b/docs/docs/references/configuration/cli/trivy_server.md index 4291496e34f1..b7ada8f2c19b 100644 --- a/docs/docs/references/configuration/cli/trivy_server.md +++ b/docs/docs/references/configuration/cli/trivy_server.md @@ -22,7 +22,7 @@ trivy server [flags] ``` --cache-backend string [EXPERIMENTAL] cache backend (e.g. redis://localhost:6379) (default "fs") --cache-ttl duration cache TTL when using redis as cache backend - --db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2") + --db-repository strings OCI repository(ies) to retrieve trivy-db in order of priority (default [ghcr.io/aquasecurity/trivy-db:2]) --download-db-only download/update vulnerability database but don't run a scan --enable-modules strings [EXPERIMENTAL] module names to enable -h, --help help for server diff --git a/docs/docs/references/configuration/cli/trivy_vm.md b/docs/docs/references/configuration/cli/trivy_vm.md index fef17624222d..9b7f6a6fef43 100644 --- a/docs/docs/references/configuration/cli/trivy_vm.md +++ b/docs/docs/references/configuration/cli/trivy_vm.md @@ -27,7 +27,7 @@ trivy vm [flags] VM_IMAGE --compliance string compliance report to generate --config-file-schemas strings specify paths to JSON configuration file schemas to determine that a file matches some configuration and pass the schema to Rego checks for type checking --custom-headers strings custom headers in client mode - --db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2") + --db-repository strings OCI repository(ies) to retrieve trivy-db in order of priority (default [ghcr.io/aquasecurity/trivy-db:2]) --dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages --detection-priority string specify the detection priority: - "precise": Prioritizes precise by minimizing false positives. @@ -52,7 +52,7 @@ trivy vm [flags] VM_IMAGE --ignore-unfixed display only fixed vulnerabilities --ignorefile string specify .trivyignore file (default ".trivyignore") --include-non-failures include successes and exceptions, available with '--scanners misconfig' - --java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db:1") + --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) --list-all-pkgs output all packages in the JSON report regardless of vulnerability --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") diff --git a/docs/docs/references/configuration/config-file.md b/docs/docs/references/configuration/config-file.md index b2b25e47689e..ae3a017fda05 100644 --- a/docs/docs/references/configuration/config-file.md +++ b/docs/docs/references/configuration/config-file.md @@ -104,7 +104,8 @@ db: download-only: false # Same as '--java-db-repository' - java-repository: "ghcr.io/aquasecurity/trivy-java-db:1" + java-repository: + - ghcr.io/aquasecurity/trivy-java-db:1 # Same as '--skip-java-db-update' java-skip-update: false @@ -113,7 +114,8 @@ db: no-progress: false # Same as '--db-repository' - repository: "ghcr.io/aquasecurity/trivy-db:2" + repository: + - ghcr.io/aquasecurity/trivy-db:2 # Same as '--skip-db-update' skip-update: false diff --git a/internal/dbtest/fake.go b/internal/dbtest/fake.go index 7943c72273e2..528c549bda7f 100644 --- a/internal/dbtest/fake.go +++ b/internal/dbtest/fake.go @@ -62,7 +62,7 @@ func NewFakeDB(t *testing.T, dbPath string, opts FakeDBOptions) *oci.Artifact { opt := ftypes.RegistryOptions{ Insecure: false, } - return oci.NewArtifact("dummy", true, opt, oci.WithImage(img)) + return oci.NewArtifact("dummy", opt, oci.WithImage(img)) } func ArchiveDir(t *testing.T, dir string) string { diff --git a/pkg/commands/app.go b/pkg/commands/app.go index a3fb6df353d9..23b44fdec553 100644 --- a/pkg/commands/app.go +++ b/pkg/commands/app.go @@ -624,7 +624,7 @@ func NewServerCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { // java-db only works on client side. serverFlags.DBFlagGroup.DownloadJavaDBOnly = nil // disable '--download-java-db-only' serverFlags.DBFlagGroup.SkipJavaDBUpdate = nil // disable '--skip-java-db-update' - serverFlags.DBFlagGroup.JavaDBRepository = nil // disable '--java-db-repository' + serverFlags.DBFlagGroup.JavaDBRepositories = nil // disable '--java-db-repository' cmd := &cobra.Command{ Use: "server [flags]", diff --git a/pkg/commands/artifact/run.go b/pkg/commands/artifact/run.go index fa454c0cd276..11c6ef6a3090 100644 --- a/pkg/commands/artifact/run.go +++ b/pkg/commands/artifact/run.go @@ -291,7 +291,7 @@ func (r *runner) initDB(ctx context.Context, opts flag.Options) error { // download the database file noProgress := opts.Quiet || opts.NoProgress - if err := operation.DownloadDB(ctx, opts.AppVersion, opts.CacheDir, opts.DBRepository, noProgress, opts.SkipDBUpdate, opts.RegistryOpts()); err != nil { + if err := operation.DownloadDB(ctx, opts.AppVersion, opts.CacheDir, opts.DBRepositories, noProgress, opts.SkipDBUpdate, opts.RegistryOpts()); err != nil { return err } @@ -321,7 +321,7 @@ func (r *runner) initJavaDB(opts flag.Options) error { // Update the Java DB noProgress := opts.Quiet || opts.NoProgress - javadb.Init(opts.CacheDir, opts.JavaDBRepository, opts.SkipJavaDBUpdate, noProgress, opts.RegistryOpts()) + javadb.Init(opts.CacheDir, opts.JavaDBRepositories, opts.SkipJavaDBUpdate, noProgress, opts.RegistryOpts()) if opts.DownloadJavaDBOnly { if err := javadb.Update(); err != nil { return xerrors.Errorf("Java DB error: %w", err) diff --git a/pkg/commands/operation/operation.go b/pkg/commands/operation/operation.go index 16aa72085949..7570c4393b69 100644 --- a/pkg/commands/operation/operation.go +++ b/pkg/commands/operation/operation.go @@ -21,14 +21,14 @@ import ( var mu sync.Mutex // DownloadDB downloads the DB -func DownloadDB(ctx context.Context, appVersion, cacheDir string, dbRepository name.Reference, quiet, skipUpdate bool, +func DownloadDB(ctx context.Context, appVersion, cacheDir string, dbRepositories []name.Reference, quiet, skipUpdate bool, opt ftypes.RegistryOptions) error { mu.Lock() defer mu.Unlock() ctx = log.WithContextPrefix(ctx, "db") dbDir := db.Dir(cacheDir) - client := db.NewClient(dbDir, quiet, db.WithDBRepository(dbRepository)) + client := db.NewClient(dbDir, quiet, db.WithDBRepository(dbRepositories)) needsUpdate, err := client.NeedsUpdate(ctx, appVersion, skipUpdate) if err != nil { return xerrors.Errorf("database error: %w", err) @@ -36,7 +36,6 @@ func DownloadDB(ctx context.Context, appVersion, cacheDir string, dbRepository n if needsUpdate { log.InfoContext(ctx, "Need to update DB") - log.InfoContext(ctx, "Downloading DB...", log.String("repository", dbRepository.String())) if err = client.Download(ctx, dbDir, opt); err != nil { return xerrors.Errorf("failed to download vulnerability DB: %w", err) } diff --git a/pkg/commands/server/run.go b/pkg/commands/server/run.go index 6c5316d7465a..e9187b3442f5 100644 --- a/pkg/commands/server/run.go +++ b/pkg/commands/server/run.go @@ -26,7 +26,7 @@ func Run(ctx context.Context, opts flag.Options) (err error) { defer cleanup() // download the database file - if err = operation.DownloadDB(ctx, opts.AppVersion, opts.CacheDir, opts.DBRepository, + if err = operation.DownloadDB(ctx, opts.AppVersion, opts.CacheDir, opts.DBRepositories, true, opts.SkipDBUpdate, opts.RegistryOpts()); err != nil { return err } @@ -50,6 +50,6 @@ func Run(ctx context.Context, opts flag.Options) (err error) { m.Register() server := rpcServer.NewServer(opts.AppVersion, opts.Listen, opts.CacheDir, opts.Token, opts.TokenHeader, - opts.PathPrefix, opts.DBRepository, opts.RegistryOpts()) + opts.PathPrefix, opts.DBRepositories, opts.RegistryOpts()) return server.ListenAndServe(ctx, cacheClient, opts.SkipDBUpdate) } diff --git a/pkg/db/db.go b/pkg/db/db.go index 344b21b86e44..f7ceb09af4c9 100644 --- a/pkg/db/db.go +++ b/pkg/db/db.go @@ -2,14 +2,13 @@ package db import ( "context" - "errors" "fmt" "os" "path/filepath" "time" "github.com/google/go-containerregistry/pkg/name" - "github.com/google/go-containerregistry/pkg/v1/remote/transport" + "github.com/samber/lo" "golang.org/x/xerrors" "github.com/aquasecurity/trivy-db/pkg/db" @@ -18,7 +17,6 @@ import ( "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/oci" - "github.com/aquasecurity/trivy/pkg/version/doc" ) const ( @@ -27,8 +25,9 @@ const ( ) var ( - DefaultRepository = fmt.Sprintf("%s:%d", "ghcr.io/aquasecurity/trivy-db", db.SchemaVersion) - defaultRepository, _ = name.NewTag(DefaultRepository) + // GitHub Container Registry + DefaultGHCRRepository = fmt.Sprintf("%s:%d", "ghcr.io/aquasecurity/trivy-db", db.SchemaVersion) + defaultGHCRRepository = lo.Must(name.NewTag(DefaultGHCRRepository)) Init = db.Init Close = db.Close @@ -36,8 +35,8 @@ var ( ) type options struct { - artifact *oci.Artifact - dbRepository name.Reference + artifact *oci.Artifact + dbRepositories []name.Reference } // Option is a functional option @@ -51,9 +50,9 @@ func WithOCIArtifact(art *oci.Artifact) Option { } // WithDBRepository takes a dbRepository -func WithDBRepository(dbRepository name.Reference) Option { +func WithDBRepository(dbRepository []name.Reference) Option { return func(opts *options) { - opts.dbRepository = dbRepository + opts.dbRepositories = dbRepository } } @@ -73,7 +72,9 @@ func Dir(cacheDir string) string { // NewClient is the factory method for DB client func NewClient(dbDir string, quiet bool, opts ...Option) *Client { o := &options{ - dbRepository: defaultRepository, + dbRepositories: []name.Reference{ + defaultGHCRRepository, + }, } for _, opt := range opts { @@ -153,20 +154,8 @@ func (c *Client) Download(ctx context.Context, dst string, opt types.RegistryOpt log.Debug("No metadata file") } - art := c.initOCIArtifact(opt) - if err := art.Download(ctx, dst, oci.DownloadOption{MediaType: dbMediaType}); err != nil { - var terr *transport.Error - if errors.As(err, &terr) { - for _, diagnostic := range terr.Errors { - // For better user experience - if diagnostic.Code == transport.DeniedErrorCode || diagnostic.Code == transport.UnauthorizedErrorCode { - // e.g. https://aquasecurity.github.io/trivy/latest/docs/references/troubleshooting/#db - log.Warnf("See %s", doc.URL("/docs/references/troubleshooting/", "db")) - break - } - } - } - return xerrors.Errorf("database download error: %w", err) + if err := c.downloadDB(ctx, opt, dst); err != nil { + return xerrors.Errorf("OCI artifact error: %w", err) } if err := c.updateDownloadedAt(ctx, dst); err != nil { @@ -201,11 +190,20 @@ func (c *Client) updateDownloadedAt(ctx context.Context, dbDir string) error { return nil } -func (c *Client) initOCIArtifact(opt types.RegistryOptions) *oci.Artifact { +func (c *Client) initArtifacts(opt types.RegistryOptions) oci.Artifacts { if c.artifact != nil { - return c.artifact + return oci.Artifacts{c.artifact} } - return oci.NewArtifact(c.dbRepository.String(), c.quiet, opt) + return oci.NewArtifacts(c.dbRepositories, opt) +} + +func (c *Client) downloadDB(ctx context.Context, opt types.RegistryOptions, dst string) error { + log.Info("Downloading vulnerability DB...") + downloadOpt := oci.DownloadOption{MediaType: dbMediaType, Quiet: c.quiet} + if err := c.initArtifacts(opt).Download(ctx, dst, downloadOpt); err != nil { + return xerrors.Errorf("failed to download vulnerability DB: %w", err) + } + return nil } func (c *Client) ShowInfo() error { diff --git a/pkg/db/db_test.go b/pkg/db/db_test.go index ff5e36e2b194..f64c0dc4a5c6 100644 --- a/pkg/db/db_test.go +++ b/pkg/db/db_test.go @@ -159,7 +159,7 @@ func TestClient_Download(t *testing.T) { { name: "invalid gzip", input: "testdata/trivy.db", - wantErr: "unexpected EOF", + wantErr: "OCI artifact error: failed to download vulnerability DB", }, } diff --git a/pkg/fanal/analyzer/analyzer_test.go b/pkg/fanal/analyzer/analyzer_test.go index 313fccda7e94..d8f93c0aa420 100644 --- a/pkg/fanal/analyzer/analyzer_test.go +++ b/pkg/fanal/analyzer/analyzer_test.go @@ -623,9 +623,9 @@ func TestAnalyzerGroup_PostAnalyze(t *testing.T) { if tt.analyzerType == analyzer.TypeJar { // init java-trivy-db with skip update - repo, err := name.NewTag(javadb.DefaultRepository) + repo, err := name.NewTag(javadb.DefaultGHCRRepository) require.NoError(t, err) - javadb.Init("./language/java/jar/testdata", repo, true, false, types.RegistryOptions{Insecure: false}) + javadb.Init("./language/java/jar/testdata", []name.Reference{repo}, true, false, types.RegistryOptions{Insecure: false}) } ctx := context.Background() diff --git a/pkg/fanal/analyzer/language/java/jar/jar_test.go b/pkg/fanal/analyzer/language/java/jar/jar_test.go index 225737166c81..146b60c1bbd6 100644 --- a/pkg/fanal/analyzer/language/java/jar/jar_test.go +++ b/pkg/fanal/analyzer/language/java/jar/jar_test.go @@ -132,9 +132,9 @@ func Test_javaLibraryAnalyzer_Analyze(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // init java-trivy-db with skip update - repo, err := name.NewTag(javadb.DefaultRepository) + repo, err := name.NewTag(javadb.DefaultGHCRRepository) require.NoError(t, err) - javadb.Init("testdata", repo, true, false, types.RegistryOptions{Insecure: false}) + javadb.Init("testdata", []name.Reference{repo}, true, false, types.RegistryOptions{Insecure: false}) a := javaLibraryAnalyzer{} ctx := context.Background() diff --git a/pkg/fanal/artifact/image/remote_sbom.go b/pkg/fanal/artifact/image/remote_sbom.go index e95fef23140c..d07d9cafe3ac 100644 --- a/pkg/fanal/artifact/image/remote_sbom.go +++ b/pkg/fanal/artifact/image/remote_sbom.go @@ -95,10 +95,11 @@ func (a Artifact) parseReferrer(ctx context.Context, repo string, desc v1.Descri defer os.RemoveAll(tmpDir) // Download SBOM to local filesystem - referrer := oci.NewArtifact(repoName, true, a.artifactOption.ImageOption.RegistryOptions) + referrer := oci.NewArtifact(repoName, a.artifactOption.ImageOption.RegistryOptions) if err = referrer.Download(ctx, tmpDir, oci.DownloadOption{ MediaType: desc.ArtifactType, Filename: fileName, + Quiet: true, }); err != nil { return artifact.Reference{}, xerrors.Errorf("SBOM download error: %w", err) } diff --git a/pkg/flag/db_flags.go b/pkg/flag/db_flags.go index 352a0094be7c..df0d6c6f5194 100644 --- a/pkg/flag/db_flags.go +++ b/pkg/flag/db_flags.go @@ -50,17 +50,17 @@ var ( ConfigName: "db.no-progress", Usage: "suppress progress bar", } - DBRepositoryFlag = Flag[string]{ + DBRepositoryFlag = Flag[[]string]{ Name: "db-repository", ConfigName: "db.repository", - Default: db.DefaultRepository, - Usage: "OCI repository to retrieve trivy-db from", + Default: []string{db.DefaultGHCRRepository}, + Usage: "OCI repository(ies) to retrieve trivy-db in order of priority", } - JavaDBRepositoryFlag = Flag[string]{ + JavaDBRepositoryFlag = Flag[[]string]{ Name: "java-db-repository", ConfigName: "db.java-repository", - Default: javadb.DefaultRepository, - Usage: "OCI repository to retrieve trivy-java-db from", + Default: []string{javadb.DefaultGHCRRepository}, + Usage: "OCI repository(ies) to retrieve trivy-java-db in order of priority", } LightFlag = Flag[bool]{ Name: "light", @@ -78,8 +78,8 @@ type DBFlagGroup struct { DownloadJavaDBOnly *Flag[bool] SkipJavaDBUpdate *Flag[bool] NoProgress *Flag[bool] - DBRepository *Flag[string] - JavaDBRepository *Flag[string] + DBRepositories *Flag[[]string] + JavaDBRepositories *Flag[[]string] Light *Flag[bool] // deprecated } @@ -90,8 +90,8 @@ type DBOptions struct { DownloadJavaDBOnly bool SkipJavaDBUpdate bool NoProgress bool - DBRepository name.Reference - JavaDBRepository name.Reference + DBRepositories []name.Reference + JavaDBRepositories []name.Reference } // NewDBFlagGroup returns a default DBFlagGroup @@ -104,8 +104,8 @@ func NewDBFlagGroup() *DBFlagGroup { SkipJavaDBUpdate: SkipJavaDBUpdateFlag.Clone(), Light: LightFlag.Clone(), NoProgress: NoProgressFlag.Clone(), - DBRepository: DBRepositoryFlag.Clone(), - JavaDBRepository: JavaDBRepositoryFlag.Clone(), + DBRepositories: DBRepositoryFlag.Clone(), + JavaDBRepositories: JavaDBRepositoryFlag.Clone(), } } @@ -121,8 +121,8 @@ func (f *DBFlagGroup) Flags() []Flagger { f.DownloadJavaDBOnly, f.SkipJavaDBUpdate, f.NoProgress, - f.DBRepository, - f.JavaDBRepository, + f.DBRepositories, + f.JavaDBRepositories, f.Light, } } @@ -147,30 +147,21 @@ func (f *DBFlagGroup) ToOptions() (DBOptions, error) { return DBOptions{}, xerrors.New("--skip-java-db-update and --download-java-db-only options can not be specified both") } - var dbRepository, javaDBRepository name.Reference - var err error - if f.DBRepository != nil { - if dbRepository, err = name.ParseReference(f.DBRepository.Value(), name.WithDefaultTag("")); err != nil { - return DBOptions{}, xerrors.Errorf("invalid db repository: %w", err) - } - // Add the schema version if the tag is not specified for backward compatibility. - if t, ok := dbRepository.(name.Tag); ok && t.TagStr() == "" { - dbRepository = t.Tag(fmt.Sprint(db.SchemaVersion)) - log.Info("Adding schema version to the DB repository for backward compatibility", - log.String("repository", dbRepository.String())) + var dbRepositories, javaDBRepositories []name.Reference + for _, repo := range f.DBRepositories.Value() { + ref, err := parseRepository(repo, db.SchemaVersion) + if err != nil { + return DBOptions{}, xerrors.Errorf("invalid DB repository: %w", err) } + dbRepositories = append(dbRepositories, ref) } - if f.JavaDBRepository != nil { - if javaDBRepository, err = name.ParseReference(f.JavaDBRepository.Value(), name.WithDefaultTag("")); err != nil { + for _, repo := range f.JavaDBRepositories.Value() { + ref, err := parseRepository(repo, javadb.SchemaVersion) + if err != nil { return DBOptions{}, xerrors.Errorf("invalid javadb repository: %w", err) } - // Add the schema version if the tag is not specified for backward compatibility. - if t, ok := javaDBRepository.(name.Tag); ok && t.TagStr() == "" { - javaDBRepository = t.Tag(fmt.Sprint(javadb.SchemaVersion)) - log.Info("Adding schema version to the Java DB repository for backward compatibility", - log.String("repository", javaDBRepository.String())) - } + javaDBRepositories = append(javaDBRepositories, ref) } return DBOptions{ @@ -180,7 +171,26 @@ func (f *DBFlagGroup) ToOptions() (DBOptions, error) { DownloadJavaDBOnly: downloadJavaDBOnly, SkipJavaDBUpdate: skipJavaDBUpdate, NoProgress: f.NoProgress.Value(), - DBRepository: dbRepository, - JavaDBRepository: javaDBRepository, + DBRepositories: dbRepositories, + JavaDBRepositories: javaDBRepositories, }, nil } + +func parseRepository(repo string, dbSchemaVersion int) (name.Reference, error) { + dbRepository, err := name.ParseReference(repo, name.WithDefaultTag("")) + if err != nil { + return nil, err + } + + // Add the schema version if the tag is not specified for backward compatibility. + t, ok := dbRepository.(name.Tag) + if !ok || t.TagStr() != "" { + return dbRepository, nil + } + + dbRepository = t.Tag(fmt.Sprint(dbSchemaVersion)) + log.Info("Adding schema version to the DB repository for backward compatibility", + log.String("repository", dbRepository.String())) + + return dbRepository, nil +} diff --git a/pkg/flag/db_flags_test.go b/pkg/flag/db_flags_test.go index 2bbd5b00198c..4f742e74ed68 100644 --- a/pkg/flag/db_flags_test.go +++ b/pkg/flag/db_flags_test.go @@ -17,31 +17,34 @@ func TestDBFlagGroup_ToOptions(t *testing.T) { SkipDBUpdate bool DownloadDBOnly bool Light bool - DBRepository string - JavaDBRepository string + DBRepository []string + JavaDBRepository []string } tests := []struct { - name string - fields fields - want flag.DBOptions - wantLogs []string - assertion require.ErrorAssertionFunc + name string + fields fields + want flag.DBOptions + wantLogs []string + wantErr string }{ { name: "happy", fields: fields{ SkipDBUpdate: true, DownloadDBOnly: false, - DBRepository: "ghcr.io/aquasecurity/trivy-db", - JavaDBRepository: "ghcr.io/aquasecurity/trivy-java-db", + DBRepository: []string{"ghcr.io/aquasecurity/trivy-db"}, + JavaDBRepository: []string{"ghcr.io/aquasecurity/trivy-java-db"}, }, want: flag.DBOptions{ - SkipDBUpdate: true, - DownloadDBOnly: false, - DBRepository: name.Tag{}, // All fields are unexported - JavaDBRepository: name.Tag{}, // All fields are unexported + SkipDBUpdate: true, + DownloadDBOnly: false, + DBRepositories: []name.Reference{name.Tag{}}, // All fields are unexported + JavaDBRepositories: []name.Reference{name.Tag{}}, // All fields are unexported + }, + wantLogs: []string{ + `Adding schema version to the DB repository for backward compatibility repository="ghcr.io/aquasecurity/trivy-db:2"`, + `Adding schema version to the DB repository for backward compatibility repository="ghcr.io/aquasecurity/trivy-java-db:1"`, }, - assertion: require.NoError, }, { name: "sad", @@ -49,25 +52,36 @@ func TestDBFlagGroup_ToOptions(t *testing.T) { SkipDBUpdate: true, DownloadDBOnly: true, }, - assertion: func(t require.TestingT, err error, msgs ...any) { - require.ErrorContains(t, err, "--skip-db-update and --download-db-only options can not be specified both") - }, + wantErr: "--skip-db-update and --download-db-only options can not be specified both", }, { name: "invalid repo", fields: fields{ SkipDBUpdate: true, DownloadDBOnly: false, - DBRepository: "foo:bar:baz", + DBRepository: []string{"foo:bar:baz"}, }, - assertion: func(t require.TestingT, err error, msgs ...any) { - require.ErrorContains(t, err, "invalid db repository") + wantErr: "invalid DB repository", + }, + { + name: "multiple repos", + fields: fields{ + SkipDBUpdate: true, + DownloadDBOnly: false, + DBRepository: []string{"ghcr.io/aquasecurity/trivy-db:2", "gallery.ecr.aws/aquasecurity/trivy-db:2"}, + JavaDBRepository: []string{"ghcr.io/aquasecurity/trivy-java-db:1", "gallery.ecr.aws/aquasecurity/trivy-java-db:1"}, + }, + want: flag.DBOptions{ + SkipDBUpdate: true, + DownloadDBOnly: false, + DBRepositories: []name.Reference{name.Tag{}, name.Tag{}}, // All fields are unexported + JavaDBRepositories: []name.Reference{name.Tag{}, name.Tag{}}, // All fields are unexported }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - out := newLogger(log.LevelWarn) + out := newLogger(log.LevelInfo) viper.Set(flag.SkipDBUpdateFlag.ConfigName, tt.fields.SkipDBUpdate) viper.Set(flag.DownloadDBOnlyFlag.ConfigName, tt.fields.DownloadDBOnly) @@ -76,13 +90,19 @@ func TestDBFlagGroup_ToOptions(t *testing.T) { // Assert options f := &flag.DBFlagGroup{ - DownloadDBOnly: flag.DownloadDBOnlyFlag.Clone(), - SkipDBUpdate: flag.SkipDBUpdateFlag.Clone(), - DBRepository: flag.DBRepositoryFlag.Clone(), - JavaDBRepository: flag.JavaDBRepositoryFlag.Clone(), + DownloadDBOnly: flag.DownloadDBOnlyFlag.Clone(), + SkipDBUpdate: flag.SkipDBUpdateFlag.Clone(), + DBRepositories: flag.DBRepositoryFlag.Clone(), + JavaDBRepositories: flag.JavaDBRepositoryFlag.Clone(), } got, err := f.ToOptions() - tt.assertion(t, err) + if tt.wantErr != "" { + require.Error(t, err) + assert.ErrorContains(t, err, tt.wantErr) + return + } + require.NoError(t, err) + assert.EqualExportedValues(t, tt.want, got) // Assert log messages diff --git a/pkg/javadb/client.go b/pkg/javadb/client.go index 9f90a37ba231..8c7aa81df9a3 100644 --- a/pkg/javadb/client.go +++ b/pkg/javadb/client.go @@ -26,12 +26,15 @@ const ( mediaType = "application/vnd.aquasec.trivy.javadb.layer.v1.tar+gzip" ) -var DefaultRepository = fmt.Sprintf("%s:%d", "ghcr.io/aquasecurity/trivy-java-db", SchemaVersion) +var ( + // GitHub Container Registry + DefaultGHCRRepository = fmt.Sprintf("%s:%d", "ghcr.io/aquasecurity/trivy-java-db", SchemaVersion) +) var updater *Updater type Updater struct { - repo name.Reference + repos []name.Reference dbDir string skip bool quiet bool @@ -40,8 +43,7 @@ type Updater struct { } func (u *Updater) Update() error { - dbDir := u.dbDir - metac := db.NewMetadata(dbDir) + metac := db.NewMetadata(u.dbDir) meta, err := metac.Get() if err != nil { @@ -55,13 +57,9 @@ func (u *Updater) Update() error { if (meta.Version != SchemaVersion || !u.isNewDB(meta)) && !u.skip { // Download DB - log.Info("Java DB Repository", log.Any("repository", u.repo)) - log.Info("Downloading the Java DB...") - // TODO: support remote options - art := oci.NewArtifact(u.repo.String(), u.quiet, u.registryOption) - if err = art.Download(context.Background(), dbDir, oci.DownloadOption{MediaType: mediaType}); err != nil { - return xerrors.Errorf("DB download error: %w", err) + if err := u.downloadDB(); err != nil { + return xerrors.Errorf("OCI artifact error: %w", err) } // Parse the newly downloaded metadata.json @@ -96,9 +94,21 @@ func (u *Updater) isNewDB(meta db.Metadata) bool { return false } -func Init(cacheDir string, javaDBRepository name.Reference, skip, quiet bool, registryOption ftypes.RegistryOptions) { +func (u *Updater) downloadDB() error { + log.Info("Downloading Java DB...") + + artifacts := oci.NewArtifacts(u.repos, u.registryOption) + downloadOpt := oci.DownloadOption{MediaType: mediaType, Quiet: u.quiet} + if err := artifacts.Download(context.Background(), u.dbDir, downloadOpt); err != nil { + return xerrors.Errorf("failed to download vulnerability DB: %w", err) + } + + return xerrors.New("failed to download Java DB from any source") +} + +func Init(cacheDir string, javaDBRepositories []name.Reference, skip, quiet bool, registryOption ftypes.RegistryOptions) { updater = &Updater{ - repo: javaDBRepository, + repos: javaDBRepositories, dbDir: dbDir(cacheDir), skip: skip, quiet: quiet, diff --git a/pkg/module/command.go b/pkg/module/command.go index 2dd955aa0e4b..a74da8384c3f 100644 --- a/pkg/module/command.go +++ b/pkg/module/command.go @@ -23,11 +23,11 @@ func Install(ctx context.Context, dir, repo string, quiet bool, opt types.Regist } log.Info("Installing the module from the repository...", log.String("repo", repo)) - art := oci.NewArtifact(repo, quiet, opt) + art := oci.NewArtifact(repo, opt) dst := filepath.Join(dir, ref.Context().Name()) log.Debug("Installing the module...", log.String("dst", dst)) - if err = art.Download(ctx, dst, oci.DownloadOption{MediaType: mediaType}); err != nil { + if err = art.Download(ctx, dst, oci.DownloadOption{MediaType: mediaType, Quiet: quiet}); err != nil { return xerrors.Errorf("module download error: %w", err) } diff --git a/pkg/oci/artifact.go b/pkg/oci/artifact.go index d8474941b3f5..345149892a4e 100644 --- a/pkg/oci/artifact.go +++ b/pkg/oci/artifact.go @@ -2,6 +2,7 @@ package oci import ( "context" + "errors" "io" "os" "path/filepath" @@ -10,11 +11,15 @@ import ( "github.com/cheggaaa/pb/v3" "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/remote/transport" + "github.com/samber/lo" "golang.org/x/xerrors" "github.com/aquasecurity/trivy/pkg/downloader" "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/remote" + "github.com/aquasecurity/trivy/pkg/version/doc" ) const ( @@ -48,7 +53,6 @@ func WithImage(img v1.Image) Option { type Artifact struct { m sync.Mutex repository string - quiet bool // For OCI registries types.RegistryOptions @@ -57,10 +61,9 @@ type Artifact struct { } // NewArtifact returns a new artifact -func NewArtifact(repo string, quiet bool, registryOpt types.RegistryOptions, opts ...Option) *Artifact { +func NewArtifact(repo string, registryOpt types.RegistryOptions, opts ...Option) *Artifact { art := &Artifact{ repository: repo, - quiet: quiet, RegistryOptions: registryOpt, } @@ -98,6 +101,7 @@ func (a *Artifact) populate(ctx context.Context, opt types.RegistryOptions) erro type DownloadOption struct { MediaType string // Accept any media type if not specified Filename string // Use the annotation if not specified + Quiet bool } func (a *Artifact) Download(ctx context.Context, dir string, opt DownloadOption) error { @@ -140,14 +144,14 @@ func (a *Artifact) Download(ctx context.Context, dir string, opt DownloadOption) return xerrors.Errorf("unacceptable media type: %s", string(layerMediaType)) } - if err = a.download(ctx, layer, fileName, dir); err != nil { + if err = a.download(ctx, layer, fileName, dir, opt.Quiet); err != nil { return xerrors.Errorf("oci download error: %w", err) } return nil } -func (a *Artifact) download(ctx context.Context, layer v1.Layer, fileName, dir string) error { +func (a *Artifact) download(ctx context.Context, layer v1.Layer, fileName, dir string, quiet bool) error { size, err := layer.Size() if err != nil { return xerrors.Errorf("size error: %w", err) @@ -161,7 +165,7 @@ func (a *Artifact) download(ctx context.Context, layer v1.Layer, fileName, dir s // Show progress bar bar := pb.Full.Start64(size) - if a.quiet { + if quiet { bar.SetWriter(io.Discard) } pr := bar.NewProxyReader(rc) @@ -207,3 +211,54 @@ func (a *Artifact) Digest(ctx context.Context) (string, error) { } return digest.String(), nil } + +type Artifacts []*Artifact + +// NewArtifacts returns a slice of artifacts. +func NewArtifacts(repos []name.Reference, opt types.RegistryOptions, opts ...Option) Artifacts { + return lo.Map(repos, func(r name.Reference, _ int) *Artifact { + return NewArtifact(r.String(), opt, opts...) + }) +} + +// Download downloads artifacts until one of them succeeds. +// Attempts to download next artifact if the first one fails due to a temporary error. +func (a Artifacts) Download(ctx context.Context, dst string, opt DownloadOption) error { + for i, art := range a { + log.Info("Downloading artifact...", log.String("repo", art.repository)) + err := art.Download(ctx, dst, opt) + if err == nil { + log.Info("Artifact successfully downloaded", log.String("repo", art.repository)) + return nil + } + + if !shouldTryOtherRepo(err) { + return xerrors.Errorf("failed to download artifact from %s: %w", art.repository, err) + } + log.Error("Failed to download artifact", log.String("repo", art.repository), log.Err(err)) + if i < len(a)-1 { + log.Info("Trying to download artifact from other repository...") + } + } + + return xerrors.New("failed to download artifact from any source") +} + +func shouldTryOtherRepo(err error) bool { + var terr *transport.Error + if !errors.As(err, &terr) { + return false + } + + for _, diagnostic := range terr.Errors { + // For better user experience + if diagnostic.Code == transport.DeniedErrorCode || diagnostic.Code == transport.UnauthorizedErrorCode { + // e.g. https://aquasecurity.github.io/trivy/latest/docs/references/troubleshooting/#db + log.Warnf("See %s", doc.URL("/docs/references/troubleshooting/", "db")) + break + } + } + + // try the following artifact only if a temporary error occurs + return terr.Temporary() +} diff --git a/pkg/oci/artifact_test.go b/pkg/oci/artifact_test.go index 7cc8cd19d3c1..a8ce6e542641 100644 --- a/pkg/oci/artifact_test.go +++ b/pkg/oci/artifact_test.go @@ -116,9 +116,10 @@ func TestArtifact_Download(t *testing.T) { }, }, nil) - artifact := oci.NewArtifact("repo", true, ftypes.RegistryOptions{}, oci.WithImage(img)) + artifact := oci.NewArtifact("repo", ftypes.RegistryOptions{}, oci.WithImage(img)) err = artifact.Download(context.Background(), tempDir, oci.DownloadOption{ MediaType: tt.mediaType, + Quiet: true, }) if tt.wantErr != "" { assert.ErrorContains(t, err, tt.wantErr) diff --git a/pkg/policy/policy.go b/pkg/policy/policy.go index c8c05cf067c7..2dbe2fc1b89a 100644 --- a/pkg/policy/policy.go +++ b/pkg/policy/policy.go @@ -92,7 +92,7 @@ func NewClient(cacheDir string, quiet bool, checkBundleRepo string, opts ...Opti func (c *Client) populateOCIArtifact(registryOpts types.RegistryOptions) { if c.artifact == nil { log.Debug("Loading check bundle", log.String("repository", c.checkBundleRepo)) - c.artifact = oci.NewArtifact(c.checkBundleRepo, c.quiet, registryOpts) + c.artifact = oci.NewArtifact(c.checkBundleRepo, registryOpts) } } @@ -101,7 +101,9 @@ func (c *Client) DownloadBuiltinPolicies(ctx context.Context, registryOpts types c.populateOCIArtifact(registryOpts) dst := c.contentDir() - if err := c.artifact.Download(ctx, dst, oci.DownloadOption{MediaType: policyMediaType}); err != nil { + if err := c.artifact.Download(ctx, dst, + oci.DownloadOption{MediaType: policyMediaType, Quiet: c.quiet}, + ); err != nil { return xerrors.Errorf("download error: %w", err) } diff --git a/pkg/policy/policy_test.go b/pkg/policy/policy_test.go index 4dbd6b9a7558..f6875eb1c9cb 100644 --- a/pkg/policy/policy_test.go +++ b/pkg/policy/policy_test.go @@ -116,7 +116,7 @@ func TestClient_LoadBuiltinPolicies(t *testing.T) { }, nil) // Mock OCI artifact - art := oci.NewArtifact("repo", true, ftypes.RegistryOptions{}, oci.WithImage(img)) + art := oci.NewArtifact("repo", ftypes.RegistryOptions{}, oci.WithImage(img)) c, err := policy.NewClient(tt.cacheDir, true, "", policy.WithOCIArtifact(art)) require.NoError(t, err) @@ -255,7 +255,7 @@ func TestClient_NeedsUpdate(t *testing.T) { require.NoError(t, err) } - art := oci.NewArtifact("repo", true, ftypes.RegistryOptions{}, oci.WithImage(img)) + art := oci.NewArtifact("repo", ftypes.RegistryOptions{}, oci.WithImage(img)) c, err := policy.NewClient(tmpDir, true, "", policy.WithOCIArtifact(art), policy.WithClock(tt.clock)) require.NoError(t, err) @@ -357,7 +357,7 @@ func TestClient_DownloadBuiltinPolicies(t *testing.T) { }, nil) // Mock OCI artifact - art := oci.NewArtifact("repo", true, ftypes.RegistryOptions{}, oci.WithImage(img)) + art := oci.NewArtifact("repo", ftypes.RegistryOptions{}, oci.WithImage(img)) c, err := policy.NewClient(tempDir, true, "", policy.WithClock(tt.clock), policy.WithOCIArtifact(art)) require.NoError(t, err) diff --git a/pkg/rpc/server/listen.go b/pkg/rpc/server/listen.go index f1e39fa1b2f0..c1bc5033530c 100644 --- a/pkg/rpc/server/listen.go +++ b/pkg/rpc/server/listen.go @@ -29,21 +29,21 @@ const updateInterval = 1 * time.Hour // Server represents Trivy server type Server struct { - appVersion string - addr string - cacheDir string - dbDir string - token string - tokenHeader string - pathPrefix string - dbRepository name.Reference + appVersion string + addr string + cacheDir string + dbDir string + token string + tokenHeader string + pathPrefix string + dbRepositories []name.Reference // For OCI registries types.RegistryOptions } // NewServer returns an instance of Server -func NewServer(appVersion, addr, cacheDir, token, tokenHeader, pathPrefix string, dbRepository name.Reference, opt types.RegistryOptions) Server { +func NewServer(appVersion, addr, cacheDir, token, tokenHeader, pathPrefix string, dbRepositories []name.Reference, opt types.RegistryOptions) Server { return Server{ appVersion: appVersion, addr: addr, @@ -52,7 +52,7 @@ func NewServer(appVersion, addr, cacheDir, token, tokenHeader, pathPrefix string token: token, tokenHeader: tokenHeader, pathPrefix: pathPrefix, - dbRepository: dbRepository, + dbRepositories: dbRepositories, RegistryOptions: opt, } } @@ -63,7 +63,7 @@ func (s Server) ListenAndServe(ctx context.Context, serverCache cache.Cache, ski dbUpdateWg := &sync.WaitGroup{} go func() { - worker := newDBWorker(db.NewClient(s.dbDir, true, db.WithDBRepository(s.dbRepository))) + worker := newDBWorker(db.NewClient(s.dbDir, true, db.WithDBRepository(s.dbRepositories))) for { time.Sleep(updateInterval) if err := worker.update(ctx, s.appVersion, s.dbDir, skipDBUpdate, dbUpdateWg, requestWg, s.RegistryOptions); err != nil { From c0e8da3828e9d3a0b30d1f6568037db8dc827765 Mon Sep 17 00:00:00 2001 From: simar7 <1254783+simar7@users.noreply.github.com> Date: Tue, 1 Oct 2024 23:20:03 -0600 Subject: [PATCH 012/108] feat(misconf): Support `--skip-*` for all included modules (#7579) Signed-off-by: nikpivkin Co-authored-by: nikpivkin --- pkg/commands/artifact/run.go | 2 + pkg/fanal/utils/utils.go | 27 ++++++ pkg/fanal/walker/fs.go | 7 +- pkg/fanal/walker/tar.go | 8 +- pkg/fanal/walker/vm.go | 5 +- pkg/fanal/walker/walk.go | 29 ------ pkg/fanal/walker/walk_test.go | 6 +- pkg/iac/scanners/terraform/options.go | 16 ++++ pkg/iac/scanners/terraform/parser/option.go | 12 +++ pkg/iac/scanners/terraform/parser/parser.go | 7 ++ pkg/iac/scanners/terraform/scanner_test.go | 99 +++++++++++++++++++++ pkg/misconf/scanner.go | 4 + 12 files changed, 181 insertions(+), 41 deletions(-) diff --git a/pkg/commands/artifact/run.go b/pkg/commands/artifact/run.go index 11c6ef6a3090..6f926519b91a 100644 --- a/pkg/commands/artifact/run.go +++ b/pkg/commands/artifact/run.go @@ -659,5 +659,7 @@ func initMisconfScannerOption(opts flag.Options) (misconf.ScannerOption, error) TfExcludeDownloaded: opts.TfExcludeDownloaded, FilePatterns: opts.FilePatterns, ConfigFileSchemas: configSchemas, + SkipFiles: opts.SkipFiles, + SkipDirs: opts.SkipDirs, }, nil } diff --git a/pkg/fanal/utils/utils.go b/pkg/fanal/utils/utils.go index 58b44510db89..463c8dd1f255 100644 --- a/pkg/fanal/utils/utils.go +++ b/pkg/fanal/utils/utils.go @@ -9,10 +9,14 @@ import ( "os" "os/exec" "path/filepath" + "strings" "unicode" + "github.com/bmatcuk/doublestar/v4" + "github.com/samber/lo" "golang.org/x/xerrors" + "github.com/aquasecurity/trivy/pkg/log" xio "github.com/aquasecurity/trivy/pkg/x/io" ) @@ -98,6 +102,29 @@ func IsBinary(content xio.ReadSeekerAt, fileSize int64) (bool, error) { return false, nil } +func CleanSkipPaths(skipPaths []string) []string { + return lo.Map(skipPaths, func(skipPath string, index int) string { + skipPath = filepath.ToSlash(filepath.Clean(skipPath)) + return strings.TrimLeft(skipPath, "/") + }) +} + +func SkipPath(path string, skipPaths []string) bool { + path = strings.TrimLeft(path, "/") + + // skip files + for _, pattern := range skipPaths { + match, err := doublestar.Match(pattern, path) + if err != nil { + return false // return early if bad pattern + } else if match { + log.Debug("Skipping path", log.String("path", path)) + return true + } + } + return false +} + func ExtractPrintableBytes(content xio.ReadSeekerAt) ([]byte, error) { const minLength = 4 // Minimum length of strings to extract var result []byte diff --git a/pkg/fanal/walker/fs.go b/pkg/fanal/walker/fs.go index f6bec84d1fc2..3ebfb7258459 100644 --- a/pkg/fanal/walker/fs.go +++ b/pkg/fanal/walker/fs.go @@ -9,6 +9,7 @@ import ( "golang.org/x/xerrors" + "github.com/aquasecurity/trivy/pkg/fanal/utils" "github.com/aquasecurity/trivy/pkg/log" xio "github.com/aquasecurity/trivy/pkg/x/io" ) @@ -53,13 +54,13 @@ func (w *FS) WalkDirFunc(root string, fn WalkFunc, opt Option) fs.WalkDirFunc { // Skip unnecessary files switch { case d.IsDir(): - if SkipPath(relPath, opt.SkipDirs) { + if utils.SkipPath(relPath, opt.SkipDirs) { return filepath.SkipDir } return nil case !d.Type().IsRegular(): return nil - case SkipPath(relPath, opt.SkipFiles): + case utils.SkipPath(relPath, opt.SkipFiles): return nil } @@ -148,7 +149,7 @@ func (w *FS) BuildSkipPaths(base string, paths []string) []string { relativePaths = append(relativePaths, relPath) } - relativePaths = CleanSkipPaths(relativePaths) + relativePaths = utils.CleanSkipPaths(relativePaths) return relativePaths } diff --git a/pkg/fanal/walker/tar.go b/pkg/fanal/walker/tar.go index 51f185d89c50..0c7d0bee9d5d 100644 --- a/pkg/fanal/walker/tar.go +++ b/pkg/fanal/walker/tar.go @@ -27,8 +27,8 @@ type LayerTar struct { func NewLayerTar(opt Option) LayerTar { return LayerTar{ - skipFiles: CleanSkipPaths(opt.SkipFiles), - skipDirs: CleanSkipPaths(opt.SkipDirs), + skipFiles: utils.CleanSkipPaths(opt.SkipFiles), + skipDirs: utils.CleanSkipPaths(opt.SkipDirs), } } @@ -63,12 +63,12 @@ func (w LayerTar) Walk(layer io.Reader, analyzeFn WalkFunc) ([]string, []string, switch hdr.Typeflag { case tar.TypeDir: - if SkipPath(filePath, w.skipDirs) { + if utils.SkipPath(filePath, w.skipDirs) { skippedDirs = append(skippedDirs, filePath) continue } case tar.TypeReg: - if SkipPath(filePath, w.skipFiles) { + if utils.SkipPath(filePath, w.skipFiles) { continue } // symlinks and hardlinks have no content in reader, skip them diff --git a/pkg/fanal/walker/vm.go b/pkg/fanal/walker/vm.go index 0bc3e87ac86d..f8ef63ce0252 100644 --- a/pkg/fanal/walker/vm.go +++ b/pkg/fanal/walker/vm.go @@ -17,6 +17,7 @@ import ( "github.com/masahiro331/go-xfs-filesystem/xfs" "golang.org/x/xerrors" + "github.com/aquasecurity/trivy/pkg/fanal/utils" "github.com/aquasecurity/trivy/pkg/fanal/vm/filesystem" "github.com/aquasecurity/trivy/pkg/log" xio "github.com/aquasecurity/trivy/pkg/x/io" @@ -131,13 +132,13 @@ func (w *VM) fsWalk(fsys fs.FS, path string, d fs.DirEntry, err error) error { pathName := strings.TrimPrefix(filepath.Clean(path), "/") switch { case fi.IsDir(): - if SkipPath(pathName, w.skipDirs) { + if utils.SkipPath(pathName, w.skipDirs) { return filepath.SkipDir } return nil case !fi.Mode().IsRegular(): return nil - case SkipPath(pathName, w.skipFiles): + case utils.SkipPath(pathName, w.skipFiles): return nil case fi.Mode()&0x1000 == 0x1000 || fi.Mode()&0x2000 == 0x2000 || diff --git a/pkg/fanal/walker/walk.go b/pkg/fanal/walker/walk.go index 52c13808d86b..0785cef41606 100644 --- a/pkg/fanal/walker/walk.go +++ b/pkg/fanal/walker/walk.go @@ -2,14 +2,8 @@ package walker import ( "os" - "path/filepath" - "strings" - - "github.com/bmatcuk/doublestar/v4" - "github.com/samber/lo" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" - "github.com/aquasecurity/trivy/pkg/log" ) const defaultSizeThreshold = int64(100) << 20 // 200MB @@ -27,26 +21,3 @@ type Option struct { } type WalkFunc func(filePath string, info os.FileInfo, opener analyzer.Opener) error - -func CleanSkipPaths(skipPaths []string) []string { - return lo.Map(skipPaths, func(skipPath string, index int) string { - skipPath = filepath.ToSlash(filepath.Clean(skipPath)) - return strings.TrimLeft(skipPath, "/") - }) -} - -func SkipPath(path string, skipPaths []string) bool { - path = strings.TrimLeft(path, "/") - - // skip files - for _, pattern := range skipPaths { - match, err := doublestar.Match(pattern, path) - if err != nil { - return false // return early if bad pattern - } else if match { - log.Debug("Skipping path", log.String("path", path)) - return true - } - } - return false -} diff --git a/pkg/fanal/walker/walk_test.go b/pkg/fanal/walker/walk_test.go index 0bac6009d04f..09d55527c0fe 100644 --- a/pkg/fanal/walker/walk_test.go +++ b/pkg/fanal/walker/walk_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" - "github.com/aquasecurity/trivy/pkg/fanal/walker" + "github.com/aquasecurity/trivy/pkg/fanal/utils" ) func TestSkipFile(t *testing.T) { @@ -64,7 +64,7 @@ func TestSkipFile(t *testing.T) { t.Run(tt.name, func(t *testing.T) { for file, want := range tt.wants { file = filepath.ToSlash(filepath.Clean(file)) - got := walker.SkipPath(file, walker.CleanSkipPaths(tt.skipFiles)) + got := utils.SkipPath(file, utils.CleanSkipPaths(tt.skipFiles)) assert.Equal(t, want, got, fmt.Sprintf("skipFiles: %s, file: %s", tt.skipFiles, file)) } }) @@ -138,7 +138,7 @@ func TestSkipDir(t *testing.T) { t.Run(tt.name, func(t *testing.T) { for dir, want := range tt.wants { dir = filepath.ToSlash(filepath.Clean(dir)) - got := walker.SkipPath(dir, walker.CleanSkipPaths(tt.skipDirs)) + got := utils.SkipPath(dir, utils.CleanSkipPaths(tt.skipDirs)) assert.Equal(t, want, got, fmt.Sprintf("defaultSkipDirs: %s, dir: %s", tt.skipDirs, dir)) } }) diff --git a/pkg/iac/scanners/terraform/options.go b/pkg/iac/scanners/terraform/options.go index d256f0a34daa..5a665a05638f 100644 --- a/pkg/iac/scanners/terraform/options.go +++ b/pkg/iac/scanners/terraform/options.go @@ -84,3 +84,19 @@ func ScannerWithConfigsFileSystem(fsys fs.FS) options.ScannerOption { } } } + +func ScannerWithSkipFiles(files []string) options.ScannerOption { + return func(s options.ConfigurableScanner) { + if tf, ok := s.(ConfigurableTerraformScanner); ok { + tf.AddParserOptions(parser.OptionWithSkipFiles(files)) + } + } +} + +func ScannerWithSkipDirs(dirs []string) options.ScannerOption { + return func(s options.ConfigurableScanner) { + if tf, ok := s.(ConfigurableTerraformScanner); ok { + tf.AddParserOptions(parser.OptionWithSkipDirs(dirs)) + } + } +} diff --git a/pkg/iac/scanners/terraform/parser/option.go b/pkg/iac/scanners/terraform/parser/option.go index 277cf2a58c61..f9ada6545225 100644 --- a/pkg/iac/scanners/terraform/parser/option.go +++ b/pkg/iac/scanners/terraform/parser/option.go @@ -49,3 +49,15 @@ func OptionWithConfigsFS(fsys fs.FS) Option { p.configsFS = fsys } } + +func OptionWithSkipFiles(files []string) Option { + return func(p *Parser) { + p.skipPaths = files + } +} + +func OptionWithSkipDirs(dirs []string) Option { + return func(p *Parser) { + p.skipPaths = dirs + } +} diff --git a/pkg/iac/scanners/terraform/parser/parser.go b/pkg/iac/scanners/terraform/parser/parser.go index 60940d6c6241..695cbee4fb25 100644 --- a/pkg/iac/scanners/terraform/parser/parser.go +++ b/pkg/iac/scanners/terraform/parser/parser.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/hcl/v2/hclparse" "github.com/zclconf/go-cty/cty" + "github.com/aquasecurity/trivy/pkg/fanal/utils" "github.com/aquasecurity/trivy/pkg/iac/ignore" "github.com/aquasecurity/trivy/pkg/iac/terraform" tfcontext "github.com/aquasecurity/trivy/pkg/iac/terraform/context" @@ -47,6 +48,7 @@ type Parser struct { skipCachedModules bool fsMap map[string]fs.FS configsFS fs.FS + skipPaths []string } // New creates a new Parser @@ -78,6 +80,7 @@ func (p *Parser) newModuleParser(moduleFS fs.FS, moduleSource, modulePath, modul mp.moduleName = moduleName mp.logger = log.WithPrefix("terraform parser").With("module", moduleName) mp.projectRoot = p.projectRoot + mp.skipPaths = p.skipPaths p.children = append(p.children, mp) for _, option := range p.options { option(mp) @@ -155,6 +158,10 @@ func (p *Parser) ParseFS(ctx context.Context, dir string) error { if info.IsDir() { continue } + if utils.SkipPath(realPath, utils.CleanSkipPaths(p.skipPaths)) { + p.logger.Debug("Skipping path based on input glob", log.FilePath(realPath), log.Any("glob", p.skipPaths)) + continue + } paths = append(paths, realPath) } sort.Strings(paths) diff --git a/pkg/iac/scanners/terraform/scanner_test.go b/pkg/iac/scanners/terraform/scanner_test.go index 39ee35e81321..a7020b4ddd06 100644 --- a/pkg/iac/scanners/terraform/scanner_test.go +++ b/pkg/iac/scanners/terraform/scanner_test.go @@ -1118,3 +1118,102 @@ func TestSkipDeprecatedGoChecks(t *testing.T) { require.Len(t, results, 1) }) } + +func TestSkipDir(t *testing.T) { + fs := testutil.CreateFS(t, map[string]string{ + "deployments/main.tf": ` +module "use_bad_configuration" { + source = "../modules" +} + +module "use_bad_configuration_2" { + source = "../modules/modules2" +} +`, + "modules/misconfig.tf": `data "aws_iam_policy_document" "bad" { + statement { + actions = [ + "apigateway:*", + ] + + resources = [ + "*", + ] + } +} + +resource "aws_iam_policy" "bad_configuration" { + name_prefix = local.setup_role_name + policy = data.aws_iam_policy_document.bad.json +} +`, + "modules/modules2/misconfig.tf": `data "aws_iam_policy_document" "bad" { + statement { + actions = [ + "apigateway:*", + ] + + resources = [ + "*", + ] + } +} + +resource "aws_iam_policy" "bad_configuration" { + name_prefix = local.setup_role_name + policy = data.aws_iam_policy_document.bad.json +} +`, + }) + + t.Run("use skip-dir option", func(t *testing.T) { + scanner := New( + options.ScannerWithIncludeDeprecatedChecks(true), + ScannerWithSkipDirs([]string{"**/modules/**"}), + ScannerWithAllDirectories(true), + ) + + results, err := scanner.ScanFS(context.TODO(), fs, "deployments") + require.NoError(t, err) + + assert.Empty(t, results) + }) + + t.Run("use skip-files option", func(t *testing.T) { + scanner := New( + options.ScannerWithIncludeDeprecatedChecks(true), + ScannerWithSkipFiles([]string{"**/modules/**/*.tf"}), + ScannerWithAllDirectories(true), + ) + + results, err := scanner.ScanFS(context.TODO(), fs, "deployments") + require.NoError(t, err) + + assert.Empty(t, results) + }) + + t.Run("non existing value for skip-files option", func(t *testing.T) { + scanner := New( + options.ScannerWithIncludeDeprecatedChecks(true), + ScannerWithSkipFiles([]string{"foo/bar*.tf"}), + ScannerWithAllDirectories(true), + ) + + results, err := scanner.ScanFS(context.TODO(), fs, "deployments") + require.NoError(t, err) + + assert.Len(t, results, 4) + }) + + t.Run("empty skip-files option", func(t *testing.T) { + scanner := New( + options.ScannerWithIncludeDeprecatedChecks(true), + ScannerWithAllDirectories(true), + ) + + results, err := scanner.ScanFS(context.TODO(), fs, "deployments") + require.NoError(t, err) + + assert.Len(t, results, 4) + }) +} diff --git a/pkg/misconf/scanner.go b/pkg/misconf/scanner.go index 78f4f4bdb158..7bec3ed6fe14 100644 --- a/pkg/misconf/scanner.go +++ b/pkg/misconf/scanner.go @@ -76,6 +76,8 @@ type ScannerOption struct { ConfigFileSchemas []*ConfigFileSchema DisabledCheckIDs []string + SkipFiles []string + SkipDirs []string } func (o *ScannerOption) Sort() { @@ -297,6 +299,8 @@ func addTFOpts(opts []options.ScannerOption, scannerOption ScannerOption) ([]opt opts = append(opts, terraform.ScannerWithAllDirectories(true), terraform.ScannerWithSkipDownloaded(scannerOption.TfExcludeDownloaded), + terraform.ScannerWithSkipFiles(scannerOption.SkipFiles), + terraform.ScannerWithSkipDirs(scannerOption.SkipDirs), ) return opts, nil From 1faf5297e72f9d4c6047451c79868daa9db5c6e7 Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Wed, 2 Oct 2024 11:04:11 +0400 Subject: [PATCH 013/108] chore: add prefixes to log messages (#7625) Signed-off-by: knqyf263 Co-authored-by: simar7 <1254783+simar7@users.noreply.github.com> --- pkg/commands/artifact/run.go | 18 ++++++++-------- pkg/commands/operation/operation.go | 14 ++++++------- pkg/db/db.go | 19 +++++++++-------- pkg/javadb/client.go | 26 +++++++++++++---------- pkg/k8s/commands/cluster.go | 17 +++++++-------- pkg/log/logger.go | 2 ++ pkg/oci/artifact.go | 8 ++++---- pkg/policy/policy.go | 32 +++++++++++++++-------------- pkg/policy/policy_test.go | 4 ++-- pkg/version/version.go | 4 +++- 10 files changed, 77 insertions(+), 67 deletions(-) diff --git a/pkg/commands/artifact/run.go b/pkg/commands/artifact/run.go index 6f926519b91a..0c9560014a0a 100644 --- a/pkg/commands/artifact/run.go +++ b/pkg/commands/artifact/run.go @@ -470,7 +470,7 @@ func filterMisconfigAnalyzers(included, all []analyzer.Type) ([]analyzer.Type, e return lo.Without(all, included...), nil } -func (r *runner) initScannerConfig(opts flag.Options) (ScannerConfig, types.ScanOptions, error) { +func (r *runner) initScannerConfig(ctx context.Context, opts flag.Options) (ScannerConfig, types.ScanOptions, error) { target := opts.Target if opts.Input != "" { target = opts.Input @@ -505,7 +505,7 @@ func (r *runner) initScannerConfig(opts flag.Options) (ScannerConfig, types.Scan var configScannerOptions misconf.ScannerOption if opts.Scanners.Enabled(types.MisconfigScanner) || opts.ImageConfigScanners.Enabled(types.MisconfigScanner) { var err error - configScannerOptions, err = initMisconfScannerOption(opts) + configScannerOptions, err = initMisconfScannerOption(ctx, opts) if err != nil { return ScannerConfig{}, types.ScanOptions{}, err } @@ -600,7 +600,7 @@ func (r *runner) initScannerConfig(opts flag.Options) (ScannerConfig, types.Scan } func (r *runner) scan(ctx context.Context, opts flag.Options, initializeScanner InitializeScanner) (types.Report, error) { - scannerConfig, scanOptions, err := r.initScannerConfig(opts) + scannerConfig, scanOptions, err := r.initScannerConfig(ctx, opts) if err != nil { return types.Report{}, err } @@ -617,20 +617,20 @@ func (r *runner) scan(ctx context.Context, opts flag.Options, initializeScanner return report, nil } -func initMisconfScannerOption(opts flag.Options) (misconf.ScannerOption, error) { - logger := log.WithPrefix(log.PrefixMisconfiguration) - logger.Info("Misconfiguration scanning is enabled") +func initMisconfScannerOption(ctx context.Context, opts flag.Options) (misconf.ScannerOption, error) { + ctx = log.WithContextPrefix(ctx, log.PrefixMisconfiguration) + log.InfoContext(ctx, "Misconfiguration scanning is enabled") var downloadedPolicyPaths []string var disableEmbedded bool - downloadedPolicyPaths, err := operation.InitBuiltinPolicies(context.Background(), opts.CacheDir, opts.Quiet, opts.SkipCheckUpdate, opts.MisconfOptions.ChecksBundleRepository, opts.RegistryOpts()) + downloadedPolicyPaths, err := operation.InitBuiltinChecks(ctx, opts.CacheDir, opts.Quiet, opts.SkipCheckUpdate, opts.MisconfOptions.ChecksBundleRepository, opts.RegistryOpts()) if err != nil { if !opts.SkipCheckUpdate { - logger.Error("Falling back to embedded checks", log.Err(err)) + log.ErrorContext(ctx, "Falling back to embedded checks", log.Err(err)) } } else { - logger.Debug("Checks successfully loaded from disk") + log.DebugContext(ctx, "Checks successfully loaded from disk") disableEmbedded = true } diff --git a/pkg/commands/operation/operation.go b/pkg/commands/operation/operation.go index 7570c4393b69..ac52eee7fb1e 100644 --- a/pkg/commands/operation/operation.go +++ b/pkg/commands/operation/operation.go @@ -26,7 +26,7 @@ func DownloadDB(ctx context.Context, appVersion, cacheDir string, dbRepositories mu.Lock() defer mu.Unlock() - ctx = log.WithContextPrefix(ctx, "db") + ctx = log.WithContextPrefix(ctx, log.PrefixVulnerabilityDB) dbDir := db.Dir(cacheDir) client := db.NewClient(dbDir, quiet, db.WithDBRepository(dbRepositories)) needsUpdate, err := client.NeedsUpdate(ctx, appVersion, skipUpdate) @@ -77,8 +77,8 @@ func DownloadVEXRepositories(ctx context.Context, opts flag.Options) error { } -// InitBuiltinPolicies downloads the built-in policies and loads them -func InitBuiltinPolicies(ctx context.Context, cacheDir string, quiet, skipUpdate bool, checkBundleRepository string, registryOpts ftypes.RegistryOptions) ([]string, error) { +// InitBuiltinChecks downloads the built-in policies and loads them +func InitBuiltinChecks(ctx context.Context, cacheDir string, quiet, skipUpdate bool, checkBundleRepository string, registryOpts ftypes.RegistryOptions) ([]string, error) { mu.Lock() defer mu.Unlock() @@ -96,14 +96,14 @@ func InitBuiltinPolicies(ctx context.Context, cacheDir string, quiet, skipUpdate } if needsUpdate { - log.Info("Need to update the built-in policies") - log.Info("Downloading the built-in policies...") - if err = client.DownloadBuiltinPolicies(ctx, registryOpts); err != nil { + log.InfoContext(ctx, "Need to update the built-in checks") + log.InfoContext(ctx, "Downloading the built-in checks...") + if err = client.DownloadBuiltinChecks(ctx, registryOpts); err != nil { return nil, xerrors.Errorf("failed to download built-in policies: %w", err) } } - policyPaths, err := client.LoadBuiltinPolicies() + policyPaths, err := client.LoadBuiltinChecks() if err != nil { if skipUpdate { msg := "No downloadable policies were loaded as --skip-check-update is enabled" diff --git a/pkg/db/db.go b/pkg/db/db.go index f7ceb09af4c9..70fbb93a5a91 100644 --- a/pkg/db/db.go +++ b/pkg/db/db.go @@ -93,22 +93,22 @@ func NewClient(dbDir string, quiet bool, opts ...Option) *Client { func (c *Client) NeedsUpdate(ctx context.Context, cliVersion string, skip bool) (bool, error) { meta, err := c.metadata.Get() if err != nil { - log.Debug("There is no valid metadata file", log.Err(err)) + log.DebugContext(ctx, "There is no valid metadata file", log.Err(err)) if skip { - log.Error("The first run cannot skip downloading DB") + log.ErrorContext(ctx, "The first run cannot skip downloading DB") return false, xerrors.New("--skip-update cannot be specified on the first run") } meta = metadata.Metadata{Version: db.SchemaVersion} } if db.SchemaVersion < meta.Version { - log.Error("The Trivy version is old. Update to the latest version.", log.String("version", cliVersion)) + log.ErrorContext(ctx, "Trivy version is old. Update to the latest version.", log.String("version", cliVersion)) return false, xerrors.Errorf("the version of DB schema doesn't match. Local DB: %d, Expected: %d", meta.Version, db.SchemaVersion) } if skip { - log.Debug("Skipping DB update...") + log.DebugContext(ctx, "Skipping DB update...") if err = c.validate(meta); err != nil { return false, xerrors.Errorf("validate error: %w", err) } @@ -116,7 +116,7 @@ func (c *Client) NeedsUpdate(ctx context.Context, cliVersion string, skip bool) } if db.SchemaVersion != meta.Version { - log.Debug("The local DB schema version does not match with supported version schema.", + log.DebugContext(ctx, "The local DB schema version does not match with supported version schema.", log.Int("local_version", meta.Version), log.Int("supported_version", db.SchemaVersion)) return true, nil } @@ -151,7 +151,7 @@ func (c *Client) isNewDB(ctx context.Context, meta metadata.Metadata) bool { func (c *Client) Download(ctx context.Context, dst string, opt types.RegistryOptions) error { // Remove the metadata file under the cache directory before downloading DB if err := c.metadata.Delete(); err != nil { - log.Debug("No metadata file") + log.DebugContext(ctx, "No metadata file") } if err := c.downloadDB(ctx, opt, dst); err != nil { @@ -198,8 +198,11 @@ func (c *Client) initArtifacts(opt types.RegistryOptions) oci.Artifacts { } func (c *Client) downloadDB(ctx context.Context, opt types.RegistryOptions, dst string) error { - log.Info("Downloading vulnerability DB...") - downloadOpt := oci.DownloadOption{MediaType: dbMediaType, Quiet: c.quiet} + log.InfoContext(ctx, "Downloading vulnerability DB...") + downloadOpt := oci.DownloadOption{ + MediaType: dbMediaType, + Quiet: c.quiet, + } if err := c.initArtifacts(opt).Download(ctx, dst, downloadOpt); err != nil { return xerrors.Errorf("failed to download vulnerability DB: %w", err) } diff --git a/pkg/javadb/client.go b/pkg/javadb/client.go index 8c7aa81df9a3..6d5b589bb5d1 100644 --- a/pkg/javadb/client.go +++ b/pkg/javadb/client.go @@ -43,6 +43,7 @@ type Updater struct { } func (u *Updater) Update() error { + ctx := log.WithContextPrefix(context.Background(), log.PrefixJavaDB) metac := db.NewMetadata(u.dbDir) meta, err := metac.Get() @@ -50,15 +51,15 @@ func (u *Updater) Update() error { if !errors.Is(err, os.ErrNotExist) { return xerrors.Errorf("Java DB metadata error: %w", err) } else if u.skip { - log.Error("The first run cannot skip downloading Java DB") + log.ErrorContext(ctx, "The first run cannot skip downloading Java DB") return xerrors.New("'--skip-java-db-update' cannot be specified on the first run") } } - if (meta.Version != SchemaVersion || !u.isNewDB(meta)) && !u.skip { + if (meta.Version != SchemaVersion || !u.isNewDB(ctx, meta)) && !u.skip { // Download DB // TODO: support remote options - if err := u.downloadDB(); err != nil { + if err := u.downloadDB(ctx); err != nil { return xerrors.Errorf("OCI artifact error: %w", err) } @@ -73,33 +74,36 @@ func (u *Updater) Update() error { if err = metac.Update(meta); err != nil { return xerrors.Errorf("Java DB metadata update error: %w", err) } - log.Info("The Java DB is cached for 3 days. If you want to update the database more frequently, " + + log.InfoContext(ctx, "Java DB is cached for 3 days. If you want to update the database more frequently, "+ `"trivy clean --java-db" command clears the DB cache.`) } return nil } -func (u *Updater) isNewDB(meta db.Metadata) bool { +func (u *Updater) isNewDB(ctx context.Context, meta db.Metadata) bool { now := time.Now().UTC() if now.Before(meta.NextUpdate) { - log.Debug("Java DB update was skipped because the local Java DB is the latest") + log.DebugContext(ctx, "Java DB update was skipped because the local Java DB is the latest") return true } if now.Before(meta.DownloadedAt.Add(time.Hour * 24)) { // 1 day - log.Debug("Java DB update was skipped because the local Java DB was downloaded during the last day") + log.DebugContext(ctx, "Java DB update was skipped because the local Java DB was downloaded during the last day") return true } return false } -func (u *Updater) downloadDB() error { - log.Info("Downloading Java DB...") +func (u *Updater) downloadDB(ctx context.Context) error { + log.InfoContext(ctx, "Downloading Java DB...") artifacts := oci.NewArtifacts(u.repos, u.registryOption) - downloadOpt := oci.DownloadOption{MediaType: mediaType, Quiet: u.quiet} - if err := artifacts.Download(context.Background(), u.dbDir, downloadOpt); err != nil { + downloadOpt := oci.DownloadOption{ + MediaType: mediaType, + Quiet: u.quiet, + } + if err := artifacts.Download(ctx, u.dbDir, downloadOpt); err != nil { return xerrors.Errorf("failed to download vulnerability DB: %w", err) } diff --git a/pkg/k8s/commands/cluster.go b/pkg/k8s/commands/cluster.go index 179a089523df..11dd5e0d3a00 100644 --- a/pkg/k8s/commands/cluster.go +++ b/pkg/k8s/commands/cluster.go @@ -37,7 +37,7 @@ func clusterRun(ctx context.Context, opts flag.Options, cluster k8s.Cluster) err trivyk8s.WithExcludeOwned(opts.ExcludeOwned), } if opts.Scanners.AnyEnabled(types.MisconfigScanner) && !opts.DisableNodeCollector { - artifacts, err = trivyk8s.New(cluster, k8sOpts...).ListArtifactAndNodeInfo(ctx, nodeCollectorOptions(opts)...) + artifacts, err = trivyk8s.New(cluster, k8sOpts...).ListArtifactAndNodeInfo(ctx, nodeCollectorOptions(ctx, opts)...) if err != nil { return xerrors.Errorf("get k8s artifacts with node info error: %w", err) } @@ -59,20 +59,17 @@ func clusterRun(ctx context.Context, opts flag.Options, cluster k8s.Cluster) err return runner.run(ctx, artifacts) } -func nodeCollectorOptions(opts flag.Options) []trivyk8s.NodeCollectorOption { +func nodeCollectorOptions(ctx context.Context, opts flag.Options) []trivyk8s.NodeCollectorOption { nodeCollectorOptions := []trivyk8s.NodeCollectorOption{ trivyk8s.WithScanJobNamespace(opts.NodeCollectorNamespace), trivyk8s.WithIgnoreLabels(opts.ExcludeNodes), trivyk8s.WithScanJobImageRef(opts.NodeCollectorImageRef), - trivyk8s.WithTolerations(opts.Tolerations)} - - contentPath, err := operation.InitBuiltinPolicies(context.Background(), - opts.CacheDir, - opts.Quiet, - opts.SkipCheckUpdate, - opts.MisconfOptions.ChecksBundleRepository, - opts.RegistryOpts()) + trivyk8s.WithTolerations(opts.Tolerations), + } + ctx = log.WithContextPrefix(ctx, log.PrefixMisconfiguration) + contentPath, err := operation.InitBuiltinChecks(ctx, opts.CacheDir, opts.Quiet, opts.SkipCheckUpdate, + opts.MisconfOptions.ChecksBundleRepository, opts.RegistryOpts()) if err != nil { log.Error("Falling back to embedded checks", log.Err(err)) nodeCollectorOptions = append(nodeCollectorOptions, diff --git a/pkg/log/logger.go b/pkg/log/logger.go index ac629d211157..2948886b9559 100644 --- a/pkg/log/logger.go +++ b/pkg/log/logger.go @@ -23,6 +23,8 @@ const ( PrefixMisconfiguration = "misconfig" PrefixSecret = "secret" PrefixLicense = "license" + PrefixVulnerabilityDB = "vulndb" + PrefixJavaDB = "javadb" ) // Logger is an alias of slog.Logger diff --git a/pkg/oci/artifact.go b/pkg/oci/artifact.go index 345149892a4e..416952578918 100644 --- a/pkg/oci/artifact.go +++ b/pkg/oci/artifact.go @@ -225,19 +225,19 @@ func NewArtifacts(repos []name.Reference, opt types.RegistryOptions, opts ...Opt // Attempts to download next artifact if the first one fails due to a temporary error. func (a Artifacts) Download(ctx context.Context, dst string, opt DownloadOption) error { for i, art := range a { - log.Info("Downloading artifact...", log.String("repo", art.repository)) + log.InfoContext(ctx, "Downloading artifact...", log.String("repo", art.repository)) err := art.Download(ctx, dst, opt) if err == nil { - log.Info("Artifact successfully downloaded", log.String("repo", art.repository)) + log.InfoContext(ctx, "Artifact successfully downloaded", log.String("repo", art.repository)) return nil } if !shouldTryOtherRepo(err) { return xerrors.Errorf("failed to download artifact from %s: %w", art.repository, err) } - log.Error("Failed to download artifact", log.String("repo", art.repository), log.Err(err)) + log.ErrorContext(ctx, "Failed to download artifact", log.String("repo", art.repository), log.Err(err)) if i < len(a)-1 { - log.Info("Trying to download artifact from other repository...") + log.InfoContext(ctx, "Trying to download artifact from other repository...") } } diff --git a/pkg/policy/policy.go b/pkg/policy/policy.go index 2dbe2fc1b89a..e277936829c5 100644 --- a/pkg/policy/policy.go +++ b/pkg/policy/policy.go @@ -89,20 +89,22 @@ func NewClient(cacheDir string, quiet bool, checkBundleRepo string, opts ...Opti }, nil } -func (c *Client) populateOCIArtifact(registryOpts types.RegistryOptions) { +func (c *Client) populateOCIArtifact(ctx context.Context, registryOpts types.RegistryOptions) { if c.artifact == nil { - log.Debug("Loading check bundle", log.String("repository", c.checkBundleRepo)) + log.DebugContext(ctx, "Loading check bundle", log.String("repository", c.checkBundleRepo)) c.artifact = oci.NewArtifact(c.checkBundleRepo, registryOpts) } } -// DownloadBuiltinPolicies download default policies from GitHub Pages -func (c *Client) DownloadBuiltinPolicies(ctx context.Context, registryOpts types.RegistryOptions) error { - c.populateOCIArtifact(registryOpts) +// DownloadBuiltinChecks download default policies from GitHub Pages +func (c *Client) DownloadBuiltinChecks(ctx context.Context, registryOpts types.RegistryOptions) error { + c.populateOCIArtifact(ctx, registryOpts) dst := c.contentDir() - if err := c.artifact.Download(ctx, dst, - oci.DownloadOption{MediaType: policyMediaType, Quiet: c.quiet}, + if err := c.artifact.Download(ctx, dst, oci.DownloadOption{ + MediaType: policyMediaType, + Quiet: c.quiet, + }, ); err != nil { return xerrors.Errorf("download error: %w", err) } @@ -111,7 +113,7 @@ func (c *Client) DownloadBuiltinPolicies(ctx context.Context, registryOpts types if err != nil { return xerrors.Errorf("digest error: %w", err) } - log.Debug("Digest of the built-in policies", log.String("digest", digest)) + log.DebugContext(ctx, "Digest of the built-in checks", log.String("digest", digest)) // Update metadata.json with the new digest and the current date if err = c.updateMetadata(digest, c.clock.Now()); err != nil { @@ -121,8 +123,8 @@ func (c *Client) DownloadBuiltinPolicies(ctx context.Context, registryOpts types return nil } -// LoadBuiltinPolicies loads default policies -func (c *Client) LoadBuiltinPolicies() ([]string, error) { +// LoadBuiltinChecks loads default policies +func (c *Client) LoadBuiltinChecks() ([]string, error) { f, err := os.Open(c.manifestPath()) if err != nil { return nil, xerrors.Errorf("manifest file open error (%s): %w", c.manifestPath(), err) @@ -150,7 +152,7 @@ func (c *Client) LoadBuiltinPolicies() ([]string, error) { // NeedsUpdate returns if the default check should be updated func (c *Client) NeedsUpdate(ctx context.Context, registryOpts types.RegistryOptions) (bool, error) { - meta, err := c.GetMetadata() + meta, err := c.GetMetadata(ctx) if err != nil { return true, nil } @@ -160,7 +162,7 @@ func (c *Client) NeedsUpdate(ctx context.Context, registryOpts types.RegistryOpt return false, nil } - c.populateOCIArtifact(registryOpts) + c.populateOCIArtifact(ctx, registryOpts) digest, err := c.artifact.Digest(ctx) if err != nil { return false, xerrors.Errorf("digest error: %w", err) @@ -211,17 +213,17 @@ func (c *Client) updateMetadata(digest string, now time.Time) error { return nil } -func (c *Client) GetMetadata() (*Metadata, error) { +func (c *Client) GetMetadata(ctx context.Context) (*Metadata, error) { f, err := os.Open(c.metadataPath()) if err != nil { - log.Debug("Failed to open the check metadata", log.Err(err)) + log.DebugContext(ctx, "Failed to open the check metadata", log.Err(err)) return nil, err } defer f.Close() var meta Metadata if err = json.NewDecoder(f).Decode(&meta); err != nil { - log.Warn("Check metadata decode error", log.Err(err)) + log.WarnContext(ctx, "Check metadata decode error", log.Err(err)) return nil, err } diff --git a/pkg/policy/policy_test.go b/pkg/policy/policy_test.go index f6875eb1c9cb..4752fa4ce7fc 100644 --- a/pkg/policy/policy_test.go +++ b/pkg/policy/policy_test.go @@ -120,7 +120,7 @@ func TestClient_LoadBuiltinPolicies(t *testing.T) { c, err := policy.NewClient(tt.cacheDir, true, "", policy.WithOCIArtifact(art)) require.NoError(t, err) - got, err := c.LoadBuiltinPolicies() + got, err := c.LoadBuiltinChecks() if tt.wantErr != "" { require.Error(t, err) assert.Contains(t, err.Error(), tt.wantErr) @@ -361,7 +361,7 @@ func TestClient_DownloadBuiltinPolicies(t *testing.T) { c, err := policy.NewClient(tempDir, true, "", policy.WithClock(tt.clock), policy.WithOCIArtifact(art)) require.NoError(t, err) - err = c.DownloadBuiltinPolicies(context.Background(), ftypes.RegistryOptions{}) + err = c.DownloadBuiltinChecks(context.Background(), ftypes.RegistryOptions{}) if tt.wantErr != "" { require.Error(t, err) assert.Contains(t, err.Error(), tt.wantErr) diff --git a/pkg/version/version.go b/pkg/version/version.go index 60ee62bd52f1..17da77a2a1bd 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -1,6 +1,7 @@ package version import ( + "context" "fmt" "path/filepath" @@ -80,7 +81,8 @@ func NewVersionInfo(cacheDir string) VersionInfo { log.Debug("Failed to instantiate policy client", log.Err(err)) } if pc != nil && err == nil { - pbMetaRaw, err := pc.GetMetadata() + ctx := log.WithContextPrefix(context.TODO(), log.PrefixMisconfiguration) + pbMetaRaw, err := pc.GetMetadata(ctx) if err != nil { log.Debug("Failed to get policy metadata", log.Err(err)) From 82e2adc6f8e68d0cc0021031170c2adb60d213ba Mon Sep 17 00:00:00 2001 From: simar7 <1254783+simar7@users.noreply.github.com> Date: Wed, 2 Oct 2024 01:49:08 -0600 Subject: [PATCH 014/108] fix(misconf): Disable deprecated checks by default (#7632) --- docs/docs/references/configuration/cli/trivy_config.md | 2 +- docs/docs/references/configuration/cli/trivy_filesystem.md | 2 +- docs/docs/references/configuration/cli/trivy_image.md | 2 +- docs/docs/references/configuration/cli/trivy_kubernetes.md | 2 +- docs/docs/references/configuration/cli/trivy_repository.md | 2 +- docs/docs/references/configuration/cli/trivy_rootfs.md | 2 +- docs/docs/references/configuration/config-file.md | 2 +- pkg/flag/rego_flags.go | 1 - 8 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/docs/references/configuration/cli/trivy_config.md b/docs/docs/references/configuration/cli/trivy_config.md index 91cd64292ea8..b32e74c1c752 100644 --- a/docs/docs/references/configuration/cli/trivy_config.md +++ b/docs/docs/references/configuration/cli/trivy_config.md @@ -31,7 +31,7 @@ trivy config [flags] DIR -h, --help help for config --ignore-policy string specify the Rego file path to evaluate each vulnerability --ignorefile string specify .trivyignore file (default ".trivyignore") - --include-deprecated-checks include deprecated checks (default true) + --include-deprecated-checks include deprecated checks --include-non-failures include successes and exceptions, available with '--scanners misconfig' --k8s-version string specify k8s version to validate outdated api by it (example: 1.21.0) --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) diff --git a/docs/docs/references/configuration/cli/trivy_filesystem.md b/docs/docs/references/configuration/cli/trivy_filesystem.md index 9944a2d618ff..620c43808e91 100644 --- a/docs/docs/references/configuration/cli/trivy_filesystem.md +++ b/docs/docs/references/configuration/cli/trivy_filesystem.md @@ -53,7 +53,7 @@ trivy filesystem [flags] PATH --ignore-unfixed display only fixed vulnerabilities --ignored-licenses strings specify a list of license to ignore --ignorefile string specify .trivyignore file (default ".trivyignore") - --include-deprecated-checks include deprecated checks (default true) + --include-deprecated-checks include deprecated checks --include-dev-deps include development dependencies in the report (supported: npm, yarn) --include-non-failures include successes and exceptions, available with '--scanners misconfig' --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) diff --git a/docs/docs/references/configuration/cli/trivy_image.md b/docs/docs/references/configuration/cli/trivy_image.md index b63daffbf4ad..cd00214793e6 100644 --- a/docs/docs/references/configuration/cli/trivy_image.md +++ b/docs/docs/references/configuration/cli/trivy_image.md @@ -71,7 +71,7 @@ trivy image [flags] IMAGE_NAME --ignorefile string specify .trivyignore file (default ".trivyignore") --image-config-scanners strings comma-separated list of what security issues to detect on container image configurations (misconfig,secret) --image-src strings image source(s) to use, in priority order (docker,containerd,podman,remote) (default [docker,containerd,podman,remote]) - --include-deprecated-checks include deprecated checks (default true) + --include-deprecated-checks include deprecated checks --include-non-failures include successes and exceptions, available with '--scanners misconfig' --input string input file path instead of image name --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) diff --git a/docs/docs/references/configuration/cli/trivy_kubernetes.md b/docs/docs/references/configuration/cli/trivy_kubernetes.md index 35190b1b82b5..8cec25ec3cba 100644 --- a/docs/docs/references/configuration/cli/trivy_kubernetes.md +++ b/docs/docs/references/configuration/cli/trivy_kubernetes.md @@ -66,7 +66,7 @@ trivy kubernetes [flags] [CONTEXT] --ignore-unfixed display only fixed vulnerabilities --ignorefile string specify .trivyignore file (default ".trivyignore") --image-src strings image source(s) to use, in priority order (docker,containerd,podman,remote) (default [docker,containerd,podman,remote]) - --include-deprecated-checks include deprecated checks (default true) + --include-deprecated-checks include deprecated checks --include-kinds strings indicate the kinds included in scanning (example: node) --include-namespaces strings indicate the namespaces included in scanning (example: kube-system) --include-non-failures include successes and exceptions, available with '--scanners misconfig' diff --git a/docs/docs/references/configuration/cli/trivy_repository.md b/docs/docs/references/configuration/cli/trivy_repository.md index 59e4f2c08cb0..959bf9a8086f 100644 --- a/docs/docs/references/configuration/cli/trivy_repository.md +++ b/docs/docs/references/configuration/cli/trivy_repository.md @@ -53,7 +53,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL) --ignore-unfixed display only fixed vulnerabilities --ignored-licenses strings specify a list of license to ignore --ignorefile string specify .trivyignore file (default ".trivyignore") - --include-deprecated-checks include deprecated checks (default true) + --include-deprecated-checks include deprecated checks --include-dev-deps include development dependencies in the report (supported: npm, yarn) --include-non-failures include successes and exceptions, available with '--scanners misconfig' --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) diff --git a/docs/docs/references/configuration/cli/trivy_rootfs.md b/docs/docs/references/configuration/cli/trivy_rootfs.md index 6022f4589446..277634ed62a9 100644 --- a/docs/docs/references/configuration/cli/trivy_rootfs.md +++ b/docs/docs/references/configuration/cli/trivy_rootfs.md @@ -56,7 +56,7 @@ trivy rootfs [flags] ROOTDIR --ignore-unfixed display only fixed vulnerabilities --ignored-licenses strings specify a list of license to ignore --ignorefile string specify .trivyignore file (default ".trivyignore") - --include-deprecated-checks include deprecated checks (default true) + --include-deprecated-checks include deprecated checks --include-non-failures include successes and exceptions, available with '--scanners misconfig' --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) --license-confidence-level float specify license classifier's confidence level (default 0.9) diff --git a/docs/docs/references/configuration/config-file.md b/docs/docs/references/configuration/config-file.md index ae3a017fda05..21d327c301b7 100644 --- a/docs/docs/references/configuration/config-file.md +++ b/docs/docs/references/configuration/config-file.md @@ -479,7 +479,7 @@ rego: data: [] # Same as '--include-deprecated-checks' - include-deprecated-checks: true + include-deprecated-checks: false # Same as '--check-namespaces' namespaces: [] diff --git a/pkg/flag/rego_flags.go b/pkg/flag/rego_flags.go index 5e856f30062e..4b291f0a5eb3 100644 --- a/pkg/flag/rego_flags.go +++ b/pkg/flag/rego_flags.go @@ -11,7 +11,6 @@ var ( Name: "include-deprecated-checks", ConfigName: "rego.include-deprecated-checks", Usage: "include deprecated checks", - Default: true, } SkipCheckUpdateFlag = Flag[bool]{ Name: "skip-check-update", From 8735242b8fb7c45039c74cf9d9d6111814515740 Mon Sep 17 00:00:00 2001 From: simar7 <1254783+simar7@users.noreply.github.com> Date: Wed, 2 Oct 2024 01:51:30 -0600 Subject: [PATCH 015/108] chore(deps): Bump trivy-checks to v1.1.0 (#7631) --- docs/docs/references/configuration/cli/trivy_config.md | 2 +- docs/docs/references/configuration/cli/trivy_filesystem.md | 2 +- docs/docs/references/configuration/cli/trivy_image.md | 2 +- docs/docs/references/configuration/cli/trivy_kubernetes.md | 2 +- docs/docs/references/configuration/cli/trivy_repository.md | 2 +- docs/docs/references/configuration/cli/trivy_rootfs.md | 2 +- docs/docs/references/configuration/cli/trivy_vm.md | 2 +- docs/docs/references/configuration/config-file.md | 2 +- go.mod | 2 +- go.sum | 4 ++-- pkg/iac/scanners/terraformplan/tfjson/test/scanner_test.go | 2 +- pkg/policy/policy.go | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/docs/references/configuration/cli/trivy_config.md b/docs/docs/references/configuration/cli/trivy_config.md index b32e74c1c752..be7854548496 100644 --- a/docs/docs/references/configuration/cli/trivy_config.md +++ b/docs/docs/references/configuration/cli/trivy_config.md @@ -13,7 +13,7 @@ trivy config [flags] DIR --cache-ttl duration cache TTL when using redis as cache backend --cf-params strings specify paths to override the CloudFormation parameters files --check-namespaces strings Rego namespaces - --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:0") + --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:1") --compliance string compliance report to generate --config-check strings specify the paths to the Rego check files or to the directories containing them, applying config files --config-data strings specify paths from which data for the Rego checks will be recursively loaded diff --git a/docs/docs/references/configuration/cli/trivy_filesystem.md b/docs/docs/references/configuration/cli/trivy_filesystem.md index 620c43808e91..0abec4990173 100644 --- a/docs/docs/references/configuration/cli/trivy_filesystem.md +++ b/docs/docs/references/configuration/cli/trivy_filesystem.md @@ -23,7 +23,7 @@ trivy filesystem [flags] PATH --cache-ttl duration cache TTL when using redis as cache backend --cf-params strings specify paths to override the CloudFormation parameters files --check-namespaces strings Rego namespaces - --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:0") + --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:1") --compliance string compliance report to generate --config-check strings specify the paths to the Rego check files or to the directories containing them, applying config files --config-data strings specify paths from which data for the Rego checks will be recursively loaded diff --git a/docs/docs/references/configuration/cli/trivy_image.md b/docs/docs/references/configuration/cli/trivy_image.md index cd00214793e6..f9095f041572 100644 --- a/docs/docs/references/configuration/cli/trivy_image.md +++ b/docs/docs/references/configuration/cli/trivy_image.md @@ -37,7 +37,7 @@ trivy image [flags] IMAGE_NAME --cache-backend string [EXPERIMENTAL] cache backend (e.g. redis://localhost:6379) (default "fs") --cache-ttl duration cache TTL when using redis as cache backend --check-namespaces strings Rego namespaces - --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:0") + --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:1") --compliance string compliance report to generate (docker-cis-1.6.0) --config-check strings specify the paths to the Rego check files or to the directories containing them, applying config files --config-data strings specify paths from which data for the Rego checks will be recursively loaded diff --git a/docs/docs/references/configuration/cli/trivy_kubernetes.md b/docs/docs/references/configuration/cli/trivy_kubernetes.md index 8cec25ec3cba..99fbc1badaa2 100644 --- a/docs/docs/references/configuration/cli/trivy_kubernetes.md +++ b/docs/docs/references/configuration/cli/trivy_kubernetes.md @@ -33,7 +33,7 @@ trivy kubernetes [flags] [CONTEXT] --cache-backend string [EXPERIMENTAL] cache backend (e.g. redis://localhost:6379) (default "fs") --cache-ttl duration cache TTL when using redis as cache backend --check-namespaces strings Rego namespaces - --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:0") + --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:1") --compliance string compliance report to generate (k8s-nsa-1.0,k8s-cis-1.23,eks-cis-1.4,rke2-cis-1.24,k8s-pss-baseline-0.1,k8s-pss-restricted-0.1) --config-check strings specify the paths to the Rego check files or to the directories containing them, applying config files --config-data strings specify paths from which data for the Rego checks will be recursively loaded diff --git a/docs/docs/references/configuration/cli/trivy_repository.md b/docs/docs/references/configuration/cli/trivy_repository.md index 959bf9a8086f..821923262dee 100644 --- a/docs/docs/references/configuration/cli/trivy_repository.md +++ b/docs/docs/references/configuration/cli/trivy_repository.md @@ -23,7 +23,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL) --cache-ttl duration cache TTL when using redis as cache backend --cf-params strings specify paths to override the CloudFormation parameters files --check-namespaces strings Rego namespaces - --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:0") + --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:1") --commit string pass the commit hash to be scanned --config-check strings specify the paths to the Rego check files or to the directories containing them, applying config files --config-data strings specify paths from which data for the Rego checks will be recursively loaded diff --git a/docs/docs/references/configuration/cli/trivy_rootfs.md b/docs/docs/references/configuration/cli/trivy_rootfs.md index 277634ed62a9..bf2575af23b9 100644 --- a/docs/docs/references/configuration/cli/trivy_rootfs.md +++ b/docs/docs/references/configuration/cli/trivy_rootfs.md @@ -26,7 +26,7 @@ trivy rootfs [flags] ROOTDIR --cache-ttl duration cache TTL when using redis as cache backend --cf-params strings specify paths to override the CloudFormation parameters files --check-namespaces strings Rego namespaces - --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:0") + --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:1") --config-check strings specify the paths to the Rego check files or to the directories containing them, applying config files --config-data strings specify paths from which data for the Rego checks will be recursively loaded --config-file-schemas strings specify paths to JSON configuration file schemas to determine that a file matches some configuration and pass the schema to Rego checks for type checking diff --git a/docs/docs/references/configuration/cli/trivy_vm.md b/docs/docs/references/configuration/cli/trivy_vm.md index 9b7f6a6fef43..14ce8b78c21a 100644 --- a/docs/docs/references/configuration/cli/trivy_vm.md +++ b/docs/docs/references/configuration/cli/trivy_vm.md @@ -23,7 +23,7 @@ trivy vm [flags] VM_IMAGE --aws-region string AWS region to scan --cache-backend string [EXPERIMENTAL] cache backend (e.g. redis://localhost:6379) (default "fs") --cache-ttl duration cache TTL when using redis as cache backend - --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:0") + --checks-bundle-repository string OCI registry URL to retrieve checks bundle from (default "ghcr.io/aquasecurity/trivy-checks:1") --compliance string compliance report to generate --config-file-schemas strings specify paths to JSON configuration file schemas to determine that a file matches some configuration and pass the schema to Rego checks for type checking --custom-headers strings custom headers in client mode diff --git a/docs/docs/references/configuration/config-file.md b/docs/docs/references/configuration/config-file.md index 21d327c301b7..9bc9d4351360 100644 --- a/docs/docs/references/configuration/config-file.md +++ b/docs/docs/references/configuration/config-file.md @@ -373,7 +373,7 @@ license: ```yaml misconfiguration: # Same as '--checks-bundle-repository' - checks-bundle-repository: "ghcr.io/aquasecurity/trivy-checks:0" + checks-bundle-repository: "ghcr.io/aquasecurity/trivy-checks:1" cloudformation: # Same as '--cf-params' diff --git a/go.mod b/go.mod index 8f5e151e6ef4..d0d10457d81c 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/aquasecurity/table v1.8.0 github.com/aquasecurity/testdocker v0.0.0-20240730042311-4642e94c7fc8 github.com/aquasecurity/tml v0.6.1 - github.com/aquasecurity/trivy-checks v0.13.1-0.20240830230553-53ddbbade784 + github.com/aquasecurity/trivy-checks v1.1.0 github.com/aquasecurity/trivy-db v0.0.0-20240910133327-7e0f4d2ed4c1 github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20240707095038-0300bc49b68b diff --git a/go.sum b/go.sum index b284e23f5722..eafeea1e389b 100644 --- a/go.sum +++ b/go.sum @@ -347,8 +347,8 @@ github.com/aquasecurity/testdocker v0.0.0-20240730042311-4642e94c7fc8 h1:b43UVqY github.com/aquasecurity/testdocker v0.0.0-20240730042311-4642e94c7fc8/go.mod h1:wXA9k3uuaxY3yu7gxrxZDPo/04FEMJtwyecdAlYrEIo= 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-checks v0.13.1-0.20240830230553-53ddbbade784 h1:1rvPiCK8uQd3sarOuZ60nwksHpxsNdrvptz4eDW/V14= -github.com/aquasecurity/trivy-checks v0.13.1-0.20240830230553-53ddbbade784/go.mod h1:Ralz7PWmR3LirHlXxVtUXc+7CFmWE82jbLk7+TPvV/0= +github.com/aquasecurity/trivy-checks v1.1.0 h1:I0tVOK8dG/KHrWsqfGNYp2uD/i0f+yS7Je31F+LIUqQ= +github.com/aquasecurity/trivy-checks v1.1.0/go.mod h1:tVzhU0gajD3GmxKPLn/BHR8ZeUquc5ajQTmAsi0kCCU= github.com/aquasecurity/trivy-db v0.0.0-20240910133327-7e0f4d2ed4c1 h1:G0gnacAORRUqz2Tm5MqivSpldY2GZ74ijhJcMsae+sA= github.com/aquasecurity/trivy-db v0.0.0-20240910133327-7e0f4d2ed4c1/go.mod h1:PYkSRx4dlgFATEt+okGwibvbxVEtqsOdH+vX/saACYE= github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 h1:JVgBIuIYbwG+ekC5lUHUpGJboPYiCcxiz06RCtz8neI= diff --git a/pkg/iac/scanners/terraformplan/tfjson/test/scanner_test.go b/pkg/iac/scanners/terraformplan/tfjson/test/scanner_test.go index 1c0ac8234862..d70280da437f 100644 --- a/pkg/iac/scanners/terraformplan/tfjson/test/scanner_test.go +++ b/pkg/iac/scanners/terraformplan/tfjson/test/scanner_test.go @@ -34,6 +34,6 @@ func Test_Scanning_Plan(t *testing.T) { } } - assert.Len(t, failedResults, 9) + assert.Len(t, failedResults, 8) } diff --git a/pkg/policy/policy.go b/pkg/policy/policy.go index e277936829c5..670588868dcb 100644 --- a/pkg/policy/policy.go +++ b/pkg/policy/policy.go @@ -18,7 +18,7 @@ import ( ) const ( - BundleVersion = 0 // Latest released MAJOR version for trivy-checks + BundleVersion = 1 // Latest released MAJOR version for trivy-checks BundleRepository = "ghcr.io/aquasecurity/trivy-checks" policyMediaType = "application/vnd.cncf.openpolicyagent.layer.v1.tar+gzip" updateInterval = 24 * time.Hour From 3e1fa2100074e840bacdd65947425b08750b7d9a Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Wed, 2 Oct 2024 12:06:14 +0200 Subject: [PATCH 016/108] fix(secret): change grafana token regex to find them without unquoted (#7627) --- pkg/fanal/secret/builtin-rules.go | 2 +- pkg/fanal/secret/scanner_test.go | 74 +++++++++++++++++++++++ pkg/fanal/secret/testdata/grafana-env.txt | 2 + 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 pkg/fanal/secret/testdata/grafana-env.txt diff --git a/pkg/fanal/secret/builtin-rules.go b/pkg/fanal/secret/builtin-rules.go index a83d8eba35ba..09bf98fd3da8 100644 --- a/pkg/fanal/secret/builtin-rules.go +++ b/pkg/fanal/secret/builtin-rules.go @@ -553,7 +553,7 @@ var builtinRules = []Rule{ Category: CategoryGrafana, Title: "Grafana API token", Severity: "MEDIUM", - Regex: MustCompile(`['\"]eyJrIjoi(?i)[a-z0-9\-_=]{72,92}['\"]`), + Regex: MustCompile(`['\"]?eyJrIjoi(?i)[a-z0-9\-_=]{72,92}['\"]?`), Keywords: []string{"eyJrIjoi"}, }, { diff --git a/pkg/fanal/secret/scanner_test.go b/pkg/fanal/secret/scanner_test.go index 44b5a2458e79..f17b1150dd4a 100644 --- a/pkg/fanal/secret/scanner_test.go +++ b/pkg/fanal/secret/scanner_test.go @@ -690,6 +690,71 @@ func TestSecretScanner(t *testing.T) { }, } + wantFindingGrafanaQuoted := types.SecretFinding{ + RuleID: "grafana-api-token", + Category: secret.CategoryGrafana, + Title: "Grafana API token", + Severity: "MEDIUM", + StartLine: 1, + EndLine: 1, + Match: "GRAFANA_TOKEN=**********************************************************************************************", + Code: types.Code{ + Lines: []types.Line{ + { + Number: 1, + Content: "GRAFANA_TOKEN=**********************************************************************************************", + Highlighted: "GRAFANA_TOKEN=**********************************************************************************************", + IsCause: true, + FirstCause: true, + LastCause: true, + }, + { + Number: 2, + Content: "GRAFANA_TOKEN=**************************************************************************************", + Highlighted: "GRAFANA_TOKEN=**************************************************************************************", + IsCause: false, + FirstCause: false, + LastCause: false, + }, + }, + }, + } + + wantFindingGrafanaUnquoted := types.SecretFinding{ + RuleID: "grafana-api-token", + Category: secret.CategoryGrafana, + Title: "Grafana API token", + Severity: "MEDIUM", + StartLine: 2, + EndLine: 2, + Match: "GRAFANA_TOKEN=********************************************************************************************", + Code: types.Code{ + Lines: []types.Line{ + { + Number: 1, + Content: "GRAFANA_TOKEN=**************************************************************************************", + Highlighted: "GRAFANA_TOKEN=**************************************************************************************", + IsCause: false, + FirstCause: false, + LastCause: false, + }, + { + Number: 2, + Content: "GRAFANA_TOKEN=********************************************************************************************", + Highlighted: "GRAFANA_TOKEN=********************************************************************************************", + IsCause: true, + FirstCause: true, + LastCause: true, + }, + { + Number: 3, + Content: "", + Highlighted: "", + }, + }, + }, + } + wantMultiLine := types.SecretFinding{ RuleID: "multi-line-secret", Category: "general", @@ -858,6 +923,15 @@ func TestSecretScanner(t *testing.T) { Findings: []types.SecretFinding{wantFindingHuggingFace}, }, }, + { + name: "find grafana secret", + configPath: filepath.Join("testdata", "config.yaml"), + inputFilePath: filepath.Join("testdata", "grafana-env.txt"), + want: types.Secret{ + FilePath: filepath.Join("testdata", "grafana-env.txt"), + Findings: []types.SecretFinding{wantFindingGrafanaUnquoted, wantFindingGrafanaQuoted}, + }, + }, { name: "find JWT token", configPath: filepath.Join("testdata", "config.yaml"), diff --git a/pkg/fanal/secret/testdata/grafana-env.txt b/pkg/fanal/secret/testdata/grafana-env.txt new file mode 100644 index 000000000000..ede29f880a6b --- /dev/null +++ b/pkg/fanal/secret/testdata/grafana-env.txt @@ -0,0 +1,2 @@ +GRAFANA_TOKEN="eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbkT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk" +GRAFANA_TOKEN=eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbkT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk From 69bf7e00ea5ab483692db830fdded26a31f03183 Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Wed, 2 Oct 2024 15:44:17 +0400 Subject: [PATCH 017/108] feat: support RPM archives (#7628) Signed-off-by: knqyf263 --- .gitignore | 3 + docs/docs/coverage/os/index.md | 4 +- docs/docs/coverage/{os => others}/bitnami.md | 4 +- docs/docs/coverage/{os => others}/conda.md | 0 docs/docs/coverage/others/rpm.md | 42 +++++ docs/docs/supply-chain/sbom.md | 2 +- go.mod | 7 +- go.sum | 14 +- magefiles/magefile.go | 4 +- mkdocs.yml | 6 +- pkg/commands/artifact/run.go | 7 + pkg/fanal/analyzer/const.go | 1 + pkg/fanal/analyzer/pkg/rpm/archive.go | 161 ++++++++++++++++++ pkg/fanal/analyzer/pkg/rpm/archive_test.go | 81 +++++++++ .../analyzer/pkg/rpm/testdata/fixture.go | 63 +++++++ pkg/fanal/applier/docker.go | 2 +- 16 files changed, 386 insertions(+), 15 deletions(-) rename docs/docs/coverage/{os => others}/bitnami.md (95%) rename docs/docs/coverage/{os => others}/conda.md (100%) create mode 100644 docs/docs/coverage/others/rpm.md create mode 100644 pkg/fanal/analyzer/pkg/rpm/archive.go create mode 100644 pkg/fanal/analyzer/pkg/rpm/archive_test.go create mode 100644 pkg/fanal/analyzer/pkg/rpm/testdata/fixture.go diff --git a/.gitignore b/.gitignore index 94254d6b0319..88070261a647 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,6 @@ dist # Signing gpg.key cmd/trivy/trivy + +# RPM +*.rpm diff --git a/docs/docs/coverage/os/index.md b/docs/docs/coverage/os/index.md index a49793db8124..8756ddc8aa95 100644 --- a/docs/docs/coverage/os/index.md +++ b/docs/docs/coverage/os/index.md @@ -28,14 +28,14 @@ Trivy supports operating systems for | [Photon OS](photon.md) | 1.0, 2.0, 3.0, 4.0 | tndf/yum/rpm | | [Debian GNU/Linux](debian.md) | 7, 8, 9, 10, 11, 12 | apt/dpkg | | [Ubuntu](ubuntu.md) | All versions supported by Canonical | apt/dpkg | -| [OSs with installed Conda](conda.md) | - | conda | +| [OSs with installed Conda](../others/conda.md) | - | conda | ## Supported container images | Container image | Supported Versions | Package Managers | |-----------------------------------------------|-------------------------------------|------------------| | [Google Distroless](google-distroless.md)[^2] | Any | apt/dpkg | -| [Bitnami](bitnami.md) | Any | - | +| [Bitnami](../others/bitnami.md) | Any | - | Each page gives more details. diff --git a/docs/docs/coverage/os/bitnami.md b/docs/docs/coverage/others/bitnami.md similarity index 95% rename from docs/docs/coverage/os/bitnami.md rename to docs/docs/coverage/others/bitnami.md index 56cfb97a4601..4773fd9ab20c 100644 --- a/docs/docs/coverage/os/bitnami.md +++ b/docs/docs/coverage/others/bitnami.md @@ -4,8 +4,8 @@ Scanning results may be inaccurate. While it is not an OS, this page describes the details of the [container images provided by Bitnami](https://github.com/bitnami/containers). -Bitnami images are based on [Debian](debian.md). -Please see [the Debian page](debian.md) for OS packages. +Bitnami images are based on [Debian](../os/debian.md). +Please see [the Debian page](../os/debian.md) for OS packages. Trivy supports the following scanners for Bitnami packages. diff --git a/docs/docs/coverage/os/conda.md b/docs/docs/coverage/others/conda.md similarity index 100% rename from docs/docs/coverage/os/conda.md rename to docs/docs/coverage/others/conda.md diff --git a/docs/docs/coverage/others/rpm.md b/docs/docs/coverage/others/rpm.md new file mode 100644 index 000000000000..092cd4396b07 --- /dev/null +++ b/docs/docs/coverage/others/rpm.md @@ -0,0 +1,42 @@ +# RPM Archives + +!!! warning "EXPERIMENTAL" + This feature might change without preserving backwards compatibility. + +Trivy supports the following scanners for RPM archives. + +| Scanner | Supported | +|:-------------:|:---------:| +| SBOM | ✓ | +| Vulnerability | ✓[^1] | +| License | ✓ | + +The table below outlines the features offered by Trivy. + +## SBOM +Trivy analyzes RPM archives matching `*.rpm`. +This feature is currently disabled by default but can be enabled with an environment variable, `TRIVY_EXPERIMENTAL_RPM_ARCHIVE`. + +```shell +TRIVY_EXPERIMENTAL_RPM_ARCHIVE=true trivy fs ./rpms --format cyclonedx --output rpms.cdx.json +``` + +!!! note + Currently, it works with `--format cyclonedx`, `--format spdx` or `--format spdx-json`. + + +## Vulnerability +Since RPM files don't have OS information, you need to generate SBOM, fill in the OS information manually and then scan the SBOM for vulnerabilities. + +For example: + +```shell +$ TRIVY_EXPERIMENTAL_RPM_ARCHIVE=true trivy fs ./rpms -f cyclonedx -o rpms.cdx.json +$ jq '(.components[] | select(.type == "operating-system")) |= (.name = "redhat" | .version = "7.9")' rpms.cdx.json > rpms-res.cdx.json +$ trivy sbom ./rpms-res.cdx.json +``` + +## License +If licenses are included in the RPM archive, Trivy extracts it. + +[^1]: Need to generate SBOM first and add OS information to that SBOM diff --git a/docs/docs/supply-chain/sbom.md b/docs/docs/supply-chain/sbom.md index ed57195b3550..f2f2d55c79a5 100644 --- a/docs/docs/supply-chain/sbom.md +++ b/docs/docs/supply-chain/sbom.md @@ -743,7 +743,7 @@ Trivy searches for SBOM files in container images with the following extensions: - `.cdx` - `.cdx.json` -In addition, Trivy automatically detects SBOM files in [Bitnami images](https://github.com/bitnami/containers), [see here](../coverage/os/bitnami.md) for more details. +In addition, Trivy automatically detects SBOM files in [Bitnami images](https://github.com/bitnami/containers), [see here](../coverage/others/bitnami.md) for more details. It is enabled in the following targets. diff --git a/go.mod b/go.mod index d0d10457d81c..d01ce2a7d4c7 100644 --- a/go.mod +++ b/go.mod @@ -98,6 +98,7 @@ require ( github.com/package-url/packageurl-go v0.1.3 github.com/quasilyte/go-ruleguard/dsl v0.3.22 github.com/samber/lo v1.47.0 + github.com/sassoftware/go-rpmutils v0.4.0 github.com/secure-systems-lab/go-securesystemslib v0.8.0 github.com/sigstore/rekor v1.3.6 github.com/sirupsen/logrus v1.9.3 @@ -152,6 +153,7 @@ require ( github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect + github.com/DataDog/zstd v1.5.5 // indirect github.com/Intevation/gval v1.3.0 // indirect github.com/Intevation/jsonpath v0.2.1 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect @@ -187,7 +189,7 @@ require ( github.com/briandowns/spinner v1.23.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect - github.com/cloudflare/circl v1.3.7 // indirect + github.com/cloudflare/circl v1.3.8 // indirect github.com/containerd/cgroups/v3 v3.0.2 // indirect github.com/containerd/containerd/api v1.7.19 // indirect github.com/containerd/continuity v0.4.3 // indirect @@ -359,7 +361,7 @@ require ( github.com/tklauser/numcpus v0.7.0 // indirect github.com/tonistiigi/go-csvvalue v0.0.0-20240710180619-ddb21b71c0b4 // indirect github.com/transparency-dev/merkle v0.0.2 // indirect - github.com/ulikunitz/xz v0.5.11 // indirect + github.com/ulikunitz/xz v0.5.12 // indirect github.com/vbatts/tar-split v0.11.5 // indirect github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect @@ -367,6 +369,7 @@ require ( github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/yashtewari/glob-intersection v0.2.0 // indirect github.com/yuin/gopher-lua v1.1.1 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect diff --git a/go.sum b/go.sum index eafeea1e389b..51d43bf95aa1 100644 --- a/go.sum +++ b/go.sum @@ -245,6 +245,8 @@ github.com/CycloneDX/cyclonedx-go v0.9.1 h1:yffaWOZsv77oTJa/SdVZYdgAgFioCeycBUKk github.com/CycloneDX/cyclonedx-go v0.9.1/go.mod h1:NE/EWvzELOFlG6+ljX/QeMlVt9VKcTwu8u0ccsACEsw= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= +github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= +github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/GoogleCloudPlatform/docker-credential-gcr v2.0.5+incompatible h1:juIaKLLVhqzP55d8x4cSVgwyQv76Z55/fRv/UBr2KkQ= github.com/GoogleCloudPlatform/docker-credential-gcr v2.0.5+incompatible/go.mod h1:BB1eHdMLYEFuFdBlRMb0N7YGVdM5s6Pt0njxgvfbGGs= github.com/Intevation/gval v1.3.0 h1:+Ze5sft5MmGbZrHj06NVUbcxCb67l9RaPTLMNr37mjw= @@ -461,8 +463,8 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= -github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= +github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI= +github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -1219,6 +1221,8 @@ github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc= github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= +github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg= +github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI= github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGqpgjJU3DYAZeI05A= github.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk= github.com/sassoftware/relic/v7 v7.6.2 h1:rS44Lbv9G9eXsukknS4mSjIAuuX+lMq/FnStgmZlUv4= @@ -1344,8 +1348,8 @@ github.com/twitchtv/twirp v8.1.3+incompatible h1:+F4TdErPgSUbMZMwp13Q/KgDVuI7HJX github.com/twitchtv/twirp v8.1.3+incompatible/go.mod h1:RRJoFSAmTEh2weEqWtpPE3vFK5YBhA6bqp2l1kfCC5A= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= -github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= -github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= +github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts= github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk= @@ -1368,6 +1372,8 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= +github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg= diff --git a/magefiles/magefile.go b/magefiles/magefile.go index b1dbcd3439ad..06ebd7e665e1 100644 --- a/magefiles/magefile.go +++ b/magefiles/magefile.go @@ -16,6 +16,8 @@ import ( "github.com/magefile/mage/sh" "github.com/magefile/mage/target" + //mage:import rpm + rpm "github.com/aquasecurity/trivy/pkg/fanal/analyzer/pkg/rpm/testdata" // Trivy packages should not be imported in Mage (see https://github.com/aquasecurity/trivy/pull/4242), // but this package doesn't have so many dependencies, and Mage is still fast. "github.com/aquasecurity/trivy/pkg/log" @@ -268,7 +270,7 @@ func compileWasmModules(pattern string) error { // Unit runs unit tests func (t Test) Unit() error { - mg.Deps(t.GenerateModules) + mg.Deps(t.GenerateModules, rpm.Fixtures) return sh.RunWithV(ENV, "go", "test", "-v", "-short", "-coverprofile=coverage.txt", "-covermode=atomic", "./...") } diff --git a/mkdocs.yml b/mkdocs.yml index 60ed74306674..abe494df9dd9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -78,7 +78,6 @@ nav: - Azure Linux (CBL-Mariner): docs/coverage/os/azure.md - CentOS: docs/coverage/os/centos.md - Chainguard: docs/coverage/os/chainguard.md - - Conda: docs/coverage/os/conda.md - Debian: docs/coverage/os/debian.md - Oracle Linux: docs/coverage/os/oracle.md - Photon OS: docs/coverage/os/photon.md @@ -88,7 +87,6 @@ nav: - Ubuntu: docs/coverage/os/ubuntu.md - Wolfi: docs/coverage/os/wolfi.md - Google Distroless (Images): docs/coverage/os/google-distroless.md - - Bitnami (Images): docs/coverage/os/bitnami.md - Language: - Overview: docs/coverage/language/index.md - C/C++: docs/coverage/language/c.md @@ -112,6 +110,10 @@ nav: - Helm: docs/coverage/iac/helm.md - Kubernetes: docs/coverage/iac/kubernetes.md - Terraform: docs/coverage/iac/terraform.md + - Others: + - Bitnami Images: docs/coverage/others/bitnami.md + - Conda: docs/coverage/others/conda.md + - RPM Archives: docs/coverage/others/rpm.md - Kubernetes: docs/coverage/kubernetes.md - Configuration: - Overview: docs/configuration/index.md diff --git a/pkg/commands/artifact/run.go b/pkg/commands/artifact/run.go index 0c9560014a0a..49c7d3b34296 100644 --- a/pkg/commands/artifact/run.go +++ b/pkg/commands/artifact/run.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "os" "slices" "github.com/hashicorp/go-multierror" @@ -457,6 +458,12 @@ func disabledAnalyzers(opts flag.Options) []analyzer.Type { analyzers = append(analyzers, analyzer.TypeExecutable) } + // Disable RPM archive analyzer unless the environment variable is set + // TODO: add '--enable-analyzers' and delete this environment variable + if os.Getenv("TRIVY_EXPERIMENTAL_RPM_ARCHIVE") == "" { + analyzers = append(analyzers, analyzer.TypeRpmArchive) + } + return analyzers } diff --git a/pkg/fanal/analyzer/const.go b/pkg/fanal/analyzer/const.go index 197c0033296e..a9733d4b6076 100644 --- a/pkg/fanal/analyzer/const.go +++ b/pkg/fanal/analyzer/const.go @@ -32,6 +32,7 @@ const ( TypeDpkg Type = "dpkg" TypeDpkgLicense Type = "dpkg-license" // For analyzing licenses TypeRpm Type = "rpm" + TypeRpmArchive Type = "rpm-archive" TypeRpmqa Type = "rpmqa" // OS Package Repository diff --git a/pkg/fanal/analyzer/pkg/rpm/archive.go b/pkg/fanal/analyzer/pkg/rpm/archive.go new file mode 100644 index 000000000000..e9db78c6c648 --- /dev/null +++ b/pkg/fanal/analyzer/pkg/rpm/archive.go @@ -0,0 +1,161 @@ +package rpm + +import ( + "context" + "errors" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/package-url/packageurl-go" + "github.com/sassoftware/go-rpmutils" + "golang.org/x/xerrors" + + "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/log" + "github.com/aquasecurity/trivy/pkg/scanner/utils" +) + +const archiveVersion = 1 + +func init() { + analyzer.RegisterAnalyzer(newRPMArchiveAnalyzer()) +} + +type rpmArchiveAnalyzer struct { + logger *log.Logger +} + +func newRPMArchiveAnalyzer() *rpmArchiveAnalyzer { + return &rpmArchiveAnalyzer{ + logger: log.WithPrefix("rpm-archive"), + } +} + +func (a *rpmArchiveAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) { + rpm, err := rpmutils.ReadRpm(input.Content) + if err != nil { + return nil, xerrors.Errorf("failed to read rpm (%s): %w", input.FilePath, err) + } + pkg, err := a.parseHeader(rpm.Header) + if err != nil { + return nil, xerrors.Errorf("failed to parse rpm header: %w", err) + } + pkg.FilePath = input.FilePath + pkg.Identifier.PURL = a.generatePURL(&pkg) + + return &analyzer.AnalysisResult{ + PackageInfos: []types.PackageInfo{ + { + FilePath: input.FilePath, + Packages: types.Packages{pkg}, + }, + }, + }, nil +} + +func (a *rpmArchiveAnalyzer) parseHeader(h *rpmutils.RpmHeader) (types.Package, error) { + if h == nil { + return types.Package{}, errors.New("rpm header is nil") + } + // Getting metadata + nevra, err := h.GetNEVRA() + if a.unexpectedError(err) != nil { + return types.Package{}, xerrors.Errorf("failed to get NEVRA: %w", err) + } + epoch, err := strconv.Atoi(nevra.Epoch) + if a.unexpectedError(err) != nil { + return types.Package{}, xerrors.Errorf("failed to convert epoch to int (%s): %w", nevra.Name, err) + } + licenses, err := h.GetStrings(rpmutils.LICENSE) + if a.unexpectedError(err) != nil { + return types.Package{}, xerrors.Errorf("failed to get licenses: %w", err) + } + srcName, srcVer, srcRel, err := a.parseSourceRPM(h) + if err != nil { + return types.Package{}, xerrors.Errorf("failed to parse source rpm: %w", err) + } + vendor, err := h.GetString(rpmutils.VENDOR) + if a.unexpectedError(err) != nil { + return types.Package{}, xerrors.Errorf("failed to get vendor: %w", err) + } + + // TODO: add the const to go-rpmutils + // cf. https://github.com/rpm-software-management/rpm/blob/rpm-4.16.0-release/lib/rpmtag.h#L375 + const modularityLabelTag = 5096 + modularityLabel, err := h.GetString(modularityLabelTag) + if a.unexpectedError(err) != nil { + return types.Package{}, xerrors.Errorf("failed to get modularitylabel: %w", err) + } + + return types.Package{ + Name: nevra.Name, + Version: nevra.Version, + Release: nevra.Release, + Epoch: epoch, + Arch: nevra.Arch, + SrcName: srcName, + SrcVersion: srcVer, + SrcRelease: srcRel, + SrcEpoch: epoch, + Licenses: licenses, + Maintainer: vendor, + Modularitylabel: modularityLabel, + }, nil +} + +func (a *rpmArchiveAnalyzer) parseSourceRPM(h *rpmutils.RpmHeader) (string, string, string, error) { + sourceRpm, err := h.GetString(rpmutils.SOURCERPM) + if a.unexpectedError(err) != nil { + return "", "", "", xerrors.Errorf("failed to get source rpm: %w", err) + } else if sourceRpm == "(none)" || sourceRpm == "" { + return "", "", "", nil + } + + srcName, srcVer, srcRel, err := splitFileName(sourceRpm) + if err != nil { + a.logger.Debug("Invalid Source RPM Found", log.String("sourcerpm", sourceRpm)) + } + return srcName, srcVer, srcRel, nil +} + +func (a *rpmArchiveAnalyzer) generatePURL(pkg *types.Package) *packageurl.PackageURL { + vendor := strings.ToLower(pkg.Maintainer) + + // TODO: Handle more vendors + var ns string + switch { + case strings.Contains(vendor, "red hat"): + ns = "redhat" + case strings.Contains(vendor, "fedora"): + ns = "fedora" + case strings.Contains(vendor, "opensuse"): + ns = "opensuse" + case strings.Contains(vendor, "suse"): + ns = "suse" + } + return packageurl.NewPackageURL(packageurl.TypeRPM, ns, pkg.Name, utils.FormatVersion(*pkg), nil, "") +} + +func (a *rpmArchiveAnalyzer) unexpectedError(err error) error { + var rerr rpmutils.NoSuchTagError + if errors.As(err, &rerr) { + a.logger.Debug("RPM tag not found", log.Err(rerr)) + return nil + } + return err +} + +func (a *rpmArchiveAnalyzer) Required(filePath string, _ os.FileInfo) bool { + return filepath.Ext(filePath) == ".rpm" +} + +func (a *rpmArchiveAnalyzer) Type() analyzer.Type { + return analyzer.TypeRpmArchive +} + +func (a *rpmArchiveAnalyzer) Version() int { + return archiveVersion +} diff --git a/pkg/fanal/analyzer/pkg/rpm/archive_test.go b/pkg/fanal/analyzer/pkg/rpm/archive_test.go new file mode 100644 index 000000000000..b9ac0a44ee53 --- /dev/null +++ b/pkg/fanal/analyzer/pkg/rpm/archive_test.go @@ -0,0 +1,81 @@ +package rpm + +import ( + "context" + "os" + "strings" + "testing" + + "github.com/package-url/packageurl-go" + "github.com/samber/lo" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/aquasecurity/trivy/pkg/fanal/analyzer" + "github.com/aquasecurity/trivy/pkg/fanal/types" +) + +func Test_rpmArchiveAnalyzer_Analyze(t *testing.T) { + tests := []struct { + name string + input analyzer.AnalysisInput + want *analyzer.AnalysisResult + wantErr require.ErrorAssertionFunc + }{ + { + name: "valid", + input: analyzer.AnalysisInput{ + FilePath: "testdata/valid.rpm", + Content: lo.Must(os.Open("testdata/socat-1.7.3.2-2.el7.x86_64.rpm")), // Must run 'mage rpm:fixtures' before this test + }, + want: &analyzer.AnalysisResult{ + PackageInfos: []types.PackageInfo{ + { + FilePath: "testdata/valid.rpm", + Packages: types.Packages{ + { + Name: "socat", + Version: "1.7.3.2", + Release: "2.el7", + Arch: "x86_64", + SrcName: "socat", + SrcVersion: "1.7.3.2", + SrcRelease: "2.el7", + FilePath: "testdata/valid.rpm", + Licenses: []string{ + "GPLv2", + }, + Maintainer: "Red Hat, Inc.", + Identifier: types.PkgIdentifier{ + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeRPM, + Namespace: "redhat", + Name: "socat", + Version: "1.7.3.2-2.el7", + }, + }, + }, + }, + }, + }, + }, + wantErr: require.NoError, + }, + { + name: "broken", + input: analyzer.AnalysisInput{ + FilePath: "testdata/broken.rpm", + Content: strings.NewReader(`broken`), + }, + wantErr: require.Error, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := newRPMArchiveAnalyzer() + got, err := a.Analyze(context.Background(), tt.input) + tt.wantErr(t, err) + assert.Equal(t, tt.want, got) + }) + } +} diff --git a/pkg/fanal/analyzer/pkg/rpm/testdata/fixture.go b/pkg/fanal/analyzer/pkg/rpm/testdata/fixture.go new file mode 100644 index 000000000000..c9b1c0779f61 --- /dev/null +++ b/pkg/fanal/analyzer/pkg/rpm/testdata/fixture.go @@ -0,0 +1,63 @@ +package rpm + +import ( + "io" + "log/slog" + "net/http" + "os" + "path" + "path/filepath" + "runtime" + + "github.com/magefile/mage/target" + "golang.org/x/xerrors" +) + +const url = "https://mirror.openshift.com/pub/openshift-v4/amd64/dependencies/rpms/4.10-beta/socat-1.7.3.2-2.el7.x86_64.rpm" + +// Fixtures downloads RPM files for unit tests +func Fixtures() error { + _, filePath, _, _ := runtime.Caller(0) + dir := filepath.Dir(filePath) + dst := filepath.Join(dir, path.Base(url)) + + // Download the file only when it is needed + if updated, err := target.Path(dst, filePath); err != nil { + return err + } else if !updated { + return nil + } + return downloadFile(url, dst) +} + +// downloadFile downloads a file from the given URL and saves it with the original filename +// TODO: move this function to a common package for Mage +func downloadFile(url, dst string) error { + slog.Info("Downloading...", slog.String("url", url)) + + // Send a GET request to the URL + resp, err := http.Get(url) + if err != nil { + return xerrors.Errorf("error sending GET request: %v", err) + } + defer resp.Body.Close() + + // Check if the response status code is OK (200) + if resp.StatusCode != http.StatusOK { + return xerrors.Errorf("unexpected status code: %d", resp.StatusCode) + } + + // Create a new file with the extracted filename + out, err := os.Create(dst) + if err != nil { + return xerrors.Errorf("error creating file: %v", err) + } + defer out.Close() + + // Copy the response body to the file + if _, err = io.Copy(out, resp.Body); err != nil { + return xerrors.Errorf("error writing to file: %v", err) + } + + return nil +} diff --git a/pkg/fanal/applier/docker.go b/pkg/fanal/applier/docker.go index c1c21f236b22..6d1967497ab9 100644 --- a/pkg/fanal/applier/docker.go +++ b/pkg/fanal/applier/docker.go @@ -220,7 +220,7 @@ func ApplyLayers(layers []ftypes.BlobInfo) ftypes.ArtifactDetail { mergedLayer.Packages[i].InstalledFiles = installedFiles } - if mergedLayer.OS.Family != "" { + if mergedLayer.OS.Family != "" && pkg.Identifier.PURL == nil { mergedLayer.Packages[i].Identifier.PURL = newPURL(mergedLayer.OS.Family, types.Metadata{OS: &mergedLayer.OS}, pkg) } mergedLayer.Packages[i].Identifier.UID = dependency.UID("", pkg) From fcaea740808d5784c120e5c5d65f5f94e1d931d4 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Thu, 3 Oct 2024 10:27:25 +0600 Subject: [PATCH 018/108] fix(misconf): not to warn about missing selectors of libraries (#7638) Signed-off-by: nikpivkin --- pkg/iac/rego/load.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pkg/iac/rego/load.go b/pkg/iac/rego/load.go index 8f1033ecfa2d..7356384a3327 100644 --- a/pkg/iac/rego/load.go +++ b/pkg/iac/rego/load.go @@ -302,14 +302,17 @@ func (s *Scanner) filterModules(retriever *MetadataRetriever) error { } if len(meta.InputOptions.Selectors) == 0 { - s.logger.Warn( - "Module has no input selectors - it will be loaded for all inputs!", - log.FilePath(module.Package.Location.File), - log.String("module", name), - ) + if !meta.Library { + s.logger.Warn( + "Module has no input selectors - it will be loaded for all inputs!", + log.FilePath(module.Package.Location.File), + log.String("module", name), + ) + } filtered[name] = module continue } + for _, selector := range meta.InputOptions.Selectors { if selector.Type == string(s.sourceType) { filtered[name] = module From d24640158fb215cc48bcc2e9fcd2676c61d0c480 Mon Sep 17 00:00:00 2001 From: Aqua Security automated builds <54269356+aqua-bot@users.noreply.github.com> Date: Thu, 3 Oct 2024 07:55:35 +0300 Subject: [PATCH 019/108] release: v0.56.0 [main] (#7447) --- .release-please-manifest.json | 2 +- CHANGELOG.md | 45 +++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index aeec62f4b8cc..208746a81be3 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1 +1 @@ -{".":"0.55.0"} +{".":"0.56.0"} diff --git a/CHANGELOG.md b/CHANGELOG.md index 19df5eb64851..afc44ba7a956 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,50 @@ # Changelog +## [0.56.0](https://github.com/aquasecurity/trivy/compare/v0.55.0...v0.56.0) (2024-10-03) + + +### Features + +* **java:** add empty versions if `pom.xml` dependency versions can't be detected ([#7520](https://github.com/aquasecurity/trivy/issues/7520)) ([b836232](https://github.com/aquasecurity/trivy/commit/b8362321adb2af220830c5de31c29978423d47da)) +* **license:** improve license normalization ([#7131](https://github.com/aquasecurity/trivy/issues/7131)) ([6472e3c](https://github.com/aquasecurity/trivy/commit/6472e3c9da2a8e7ba41598a45c80df8f18e57d4c)) +* **misconf:** add ability to disable checks by ID ([#7536](https://github.com/aquasecurity/trivy/issues/7536)) ([ef0a27d](https://github.com/aquasecurity/trivy/commit/ef0a27d515ff80762bf1959d44a8bde017ae06ec)) +* **misconf:** Register checks only when needed ([#7435](https://github.com/aquasecurity/trivy/issues/7435)) ([f768d3a](https://github.com/aquasecurity/trivy/commit/f768d3a767a99a86b0372f19d9f49a2de35dbe59)) +* **misconf:** Support `--skip-*` for all included modules ([#7579](https://github.com/aquasecurity/trivy/issues/7579)) ([c0e8da3](https://github.com/aquasecurity/trivy/commit/c0e8da3828e9d3a0b30d1f6568037db8dc827765)) +* **secret:** enhance secret scanning for python binary files ([#7223](https://github.com/aquasecurity/trivy/issues/7223)) ([60725f8](https://github.com/aquasecurity/trivy/commit/60725f879ba014c5c57583db6afc290b78facae8)) +* support multiple DB repositories for vulnerability and Java DB ([#7605](https://github.com/aquasecurity/trivy/issues/7605)) ([3562529](https://github.com/aquasecurity/trivy/commit/3562529ddfb26d301311ed450c192e17011353df)) +* support RPM archives ([#7628](https://github.com/aquasecurity/trivy/issues/7628)) ([69bf7e0](https://github.com/aquasecurity/trivy/commit/69bf7e00ea5ab483692db830fdded26a31f03183)) +* **suse:** added SUSE Linux Enterprise Micro support ([#7294](https://github.com/aquasecurity/trivy/issues/7294)) ([efdb68d](https://github.com/aquasecurity/trivy/commit/efdb68d3b9ddf9dfaf45ea5855b31c43a4366bab)) + + +### Bug Fixes + +* allow access to '..' in mapfs ([#7575](https://github.com/aquasecurity/trivy/issues/7575)) ([a8fbe46](https://github.com/aquasecurity/trivy/commit/a8fbe46119adbd89f827a75c75b9e97d392f1842)) +* **db:** check `DownloadedAt` for `trivy-java-db` ([#7592](https://github.com/aquasecurity/trivy/issues/7592)) ([13ef3e7](https://github.com/aquasecurity/trivy/commit/13ef3e7d62ba2bcb3a04d7b44f79b1299674b480)) +* **java:** use `dependencyManagement` from root/child pom's for dependencies from parents ([#7497](https://github.com/aquasecurity/trivy/issues/7497)) ([5442949](https://github.com/aquasecurity/trivy/commit/54429497e7d6a87eac236771d4efb8a5a7faaac5)) +* **license:** stop spliting a long license text ([#7336](https://github.com/aquasecurity/trivy/issues/7336)) ([4926da7](https://github.com/aquasecurity/trivy/commit/4926da79de901fba73819d71845ec0355b68ae0f)) +* **misconf:** Disable deprecated checks by default ([#7632](https://github.com/aquasecurity/trivy/issues/7632)) ([82e2adc](https://github.com/aquasecurity/trivy/commit/82e2adc6f8e68d0cc0021031170c2adb60d213ba)) +* **misconf:** disable DS016 check for image history analyzer ([#7540](https://github.com/aquasecurity/trivy/issues/7540)) ([de40df9](https://github.com/aquasecurity/trivy/commit/de40df9408d6d856a3ad384ec9f086edce3aa382)) +* **misconf:** escape all special sequences ([#7558](https://github.com/aquasecurity/trivy/issues/7558)) ([ea0cf03](https://github.com/aquasecurity/trivy/commit/ea0cf0379aff0348fde87356dab37947800fc1b6)) +* **misconf:** Fix logging typo ([#7473](https://github.com/aquasecurity/trivy/issues/7473)) ([56db43c](https://github.com/aquasecurity/trivy/commit/56db43c24f4f6be92891be85faaf9492cad516ac)) +* **misconf:** Fixed scope for China Cloud ([#7560](https://github.com/aquasecurity/trivy/issues/7560)) ([37d549e](https://github.com/aquasecurity/trivy/commit/37d549e5b86a1c5dce6710fbfd2310aec9abe949)) +* **misconf:** not to warn about missing selectors of libraries ([#7638](https://github.com/aquasecurity/trivy/issues/7638)) ([fcaea74](https://github.com/aquasecurity/trivy/commit/fcaea740808d5784c120e5c5d65f5f94e1d931d4)) +* **oracle:** Update EOL date for Oracle 7 ([#7480](https://github.com/aquasecurity/trivy/issues/7480)) ([dd0a64a](https://github.com/aquasecurity/trivy/commit/dd0a64a1cf0cd76e6f81e3ff55fa6ccb95ce3c3d)) +* **report:** change a receiver of MarshalJSON ([#7483](https://github.com/aquasecurity/trivy/issues/7483)) ([927c6e0](https://github.com/aquasecurity/trivy/commit/927c6e0c9d4d4a3f1be00f0f661c1d18325d9440)) +* **report:** fix error with unmarshal of `ExperimentalModifiedFindings` ([#7463](https://github.com/aquasecurity/trivy/issues/7463)) ([7ff9aff](https://github.com/aquasecurity/trivy/commit/7ff9aff2739b2eee4a98175b98914795e4077060)) +* **sbom:** export bom-ref when converting a package to a component ([#7340](https://github.com/aquasecurity/trivy/issues/7340)) ([5dd94eb](https://github.com/aquasecurity/trivy/commit/5dd94ebc1ffe3f1df511dee6381f92a5daefadf2)) +* **sbom:** parse type `framework` as `library` when unmarshalling `CycloneDX` files ([#7527](https://github.com/aquasecurity/trivy/issues/7527)) ([aeb7039](https://github.com/aquasecurity/trivy/commit/aeb7039d7ce090e243d29f0bf16c9e4e24252a01)) +* **secret:** change grafana token regex to find them without unquoted ([#7627](https://github.com/aquasecurity/trivy/issues/7627)) ([3e1fa21](https://github.com/aquasecurity/trivy/commit/3e1fa2100074e840bacdd65947425b08750b7d9a)) + + +### Performance Improvements + +* **misconf:** use port ranges instead of enumeration ([#7549](https://github.com/aquasecurity/trivy/issues/7549)) ([1f9fc13](https://github.com/aquasecurity/trivy/commit/1f9fc13da4a1e7c76c978e4f8e119bfd61a0480e)) + + +### Reverts + +* **java:** stop supporting of `test` scope for `pom.xml` files ([#7488](https://github.com/aquasecurity/trivy/issues/7488)) ([b0222fe](https://github.com/aquasecurity/trivy/commit/b0222feeb586ec59904bb321fda8f3f22496d07b)) + ## [0.55.0](https://github.com/aquasecurity/trivy/compare/v0.54.0...v0.55.0) (2024-09-03) From cb0b3a9279b31810ecd686a385e5140e567ce86f Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Thu, 3 Oct 2024 06:56:59 +0200 Subject: [PATCH 020/108] feat(cli): error out when ignore file cannot be found (#7624) --- pkg/flag/report_flags.go | 6 ++++++ pkg/flag/report_flags_test.go | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/pkg/flag/report_flags.go b/pkg/flag/report_flags.go index 67d553b65553..d69443e89547 100644 --- a/pkg/flag/report_flags.go +++ b/pkg/flag/report_flags.go @@ -6,6 +6,7 @@ import ( "github.com/mattn/go-shellwords" "github.com/samber/lo" + "github.com/spf13/viper" "golang.org/x/xerrors" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" @@ -14,6 +15,7 @@ import ( "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/result" "github.com/aquasecurity/trivy/pkg/types" + "github.com/aquasecurity/trivy/pkg/utils/fsutils" xstrings "github.com/aquasecurity/trivy/pkg/x/strings" ) @@ -238,6 +240,10 @@ func (f *ReportFlagGroup) ToOptions() (ReportOptions, error) { } } + if viper.IsSet(f.IgnoreFile.ConfigName) && !fsutils.FileExists(f.IgnoreFile.Value()) { + return ReportOptions{}, xerrors.Errorf("ignore file not found: %s", f.IgnoreFile.Value()) + } + return ReportOptions{ Format: format, ReportFormat: f.ReportFormat.Value(), diff --git a/pkg/flag/report_flags_test.go b/pkg/flag/report_flags_test.go index 9440bf373905..b113d7c62f97 100644 --- a/pkg/flag/report_flags_test.go +++ b/pkg/flag/report_flags_test.go @@ -209,4 +209,16 @@ func TestReportFlagGroup_ToOptions(t *testing.T) { assert.Equal(t, tt.wantLogs, out.Messages(), tt.name) }) } + + t.Run("Error on non existing ignore file", func(t *testing.T) { + t.Cleanup(viper.Reset) + + setValue(flag.IgnoreFileFlag.ConfigName, string("doesntexist")) + f := &flag.ReportFlagGroup{ + IgnoreFile: flag.IgnoreFileFlag.Clone(), + } + + _, err := f.ToOptions() + assert.ErrorContains(t, err, "ignore file not found: doesntexist") + }) } From 2c87f0cb794acd77446a273582ba1a45b9f18980 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Thu, 3 Oct 2024 18:11:58 +0600 Subject: [PATCH 021/108] fix(db): fix javadb downloading error handling (#7642) Signed-off-by: nikpivkin --- pkg/javadb/client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/javadb/client.go b/pkg/javadb/client.go index 6d5b589bb5d1..835730109b02 100644 --- a/pkg/javadb/client.go +++ b/pkg/javadb/client.go @@ -104,10 +104,10 @@ func (u *Updater) downloadDB(ctx context.Context) error { Quiet: u.quiet, } if err := artifacts.Download(ctx, u.dbDir, downloadOpt); err != nil { - return xerrors.Errorf("failed to download vulnerability DB: %w", err) + return xerrors.Errorf("failed to download Java DB: %w", err) } - return xerrors.New("failed to download Java DB from any source") + return nil } func Init(cacheDir string, javaDBRepositories []name.Reference, skip, quiet bool, registryOption ftypes.RegistryOptions) { From ab3a3b2e6ed15db90967084fac825ddb2f50e70d Mon Sep 17 00:00:00 2001 From: Pierre Baumard Date: Mon, 7 Oct 2024 09:41:01 +0200 Subject: [PATCH 022/108] docs(report): Improve SARIF reporting doc (#7655) --- docs/docs/configuration/reporting.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/docs/configuration/reporting.md b/docs/docs/configuration/reporting.md index 17bf0b864283..fcbde0b106ff 100644 --- a/docs/docs/configuration/reporting.md +++ b/docs/docs/configuration/reporting.md @@ -5,7 +5,7 @@ Trivy supports the following formats: - Table - JSON -- [SARIF](https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning) +- [SARIF][sarif-home] - Template - SBOM - GitHub dependency snapshot @@ -252,13 +252,15 @@ $ trivy image -f json -o results.json golang:1.12-alpine | Secret | ✓ | | License | ✓ | -[SARIF][sarif] can be generated with the `--format sarif` flag. +[SARIF][sarif-home] (Static Analysis Results Interchange Format) complying with [SARIF 2.1.0 OASIS standard][sarif-spec] can be generated with the `--format sarif` flag. ``` $ trivy image --format sarif -o report.sarif golang:1.12-alpine ``` -This SARIF file can be uploaded to GitHub code scanning results, and there is a [Trivy GitHub Action][action] for automating this process. +This SARIF file can be uploaded to several platforms, including: +- [GitHub code scanning results][sarif-github], and there is a [Trivy GitHub Action][action] for automating this process; +- [SonarQube][sarif-sonar]. ### GitHub dependency snapshot Trivy supports the following packages. @@ -430,7 +432,10 @@ $ trivy convert --format table --severity CRITICAL result.json [cargo-auditable]: https://github.com/rust-secure-code/cargo-auditable/ [action]: https://github.com/aquasecurity/trivy-action [asff]: ../../tutorials/integrations/aws-security-hub.md -[sarif]: https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/managing-results-from-code-scanning +[sarif-home]: https://sarifweb.azurewebsites.net +[sarif-spec]: https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html +[sarif-github]: https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning +[sarif-sonar]: https://docs.sonarsource.com/sonarqube/latest/analyzing-source-code/importing-external-issues/importing-issues-from-sarif-reports/ [sprig]: http://masterminds.github.io/sprig/ [github-sbom]: https://docs.github.com/en/rest/dependency-graph/dependency-submission?apiVersion=2022-11-28#about-dependency-submissions [github-sbom-submit]: https://docs.github.com/en/rest/dependency-graph/dependency-submission?apiVersion=2022-11-28#create-a-snapshot-of-dependencies-for-a-repository @@ -450,4 +455,4 @@ $ trivy convert --format table --severity CRITICAL result.json [gradle-lockfile]: ../coverage/language/java.md#gradlelock [sbt-lockfile]: ../coverage/language/java.md#sbt [pubspec-lock]: ../coverage/language/dart.md#dart -[cargo-binaries]: ../coverage/language/rust.md#binaries \ No newline at end of file +[cargo-binaries]: ../coverage/language/rust.md#binaries From 015bb885ac414b91201fa9791eead395d878149c Mon Sep 17 00:00:00 2001 From: Pierre Baumard Date: Mon, 7 Oct 2024 13:31:59 +0200 Subject: [PATCH 023/108] fix(report): Fix invalid URI in SARIF report (#7645) Co-authored-by: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> --- pkg/report/sarif.go | 37 ++++++++++++++++++++++++------------- pkg/report/sarif_test.go | 34 +++++++++++++++++----------------- 2 files changed, 41 insertions(+), 30 deletions(-) diff --git a/pkg/report/sarif.go b/pkg/report/sarif.go index a94dcccb2c9b..3bc3344611e2 100644 --- a/pkg/report/sarif.go +++ b/pkg/report/sarif.go @@ -5,6 +5,7 @@ import ( "fmt" "html" "io" + "net/url" "path/filepath" "regexp" "strings" @@ -15,6 +16,7 @@ import ( "github.com/aquasecurity/trivy/pkg/fanal/artifact" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/types" ) @@ -61,9 +63,9 @@ type sarifData struct { helpMarkdown string resourceClass types.ResultClass severity string - url string + url *url.URL resultIndex int - artifactLocation string + artifactLocation *url.URL locationMessage string message string cvssScore string @@ -97,8 +99,8 @@ func (sw *SarifWriter) addSarifRule(data *sarifData) { "precision": "very-high", "security-severity": data.cvssScore, }) - if data.url != "" { - r.WithHelpURI(data.url) + if data.url != nil && data.url.String() != "" { + r.WithHelpURI(data.url.String()) } } @@ -109,7 +111,7 @@ func (sw *SarifWriter) addSarifResult(data *sarifData) { WithRuleIndex(data.resultIndex). WithMessage(sarif.NewTextMessage(data.message)). WithLevel(toSarifErrorLevel(data.severity)). - WithLocations(toSarifLocations(data.locations, data.artifactLocation, data.locationMessage)) + WithLocations(toSarifLocations(data.locations, data.artifactLocation.String(), data.locationMessage)) sw.run.AddResult(result) } @@ -163,9 +165,9 @@ func (sw *SarifWriter) Write(ctx context.Context, report types.Report) error { vulnerabilityId: vuln.VulnerabilityID, severity: vuln.Severity, cvssScore: getCVSSScore(vuln), - url: vuln.PrimaryURL, + url: toUri(vuln.PrimaryURL), resourceClass: res.Class, - artifactLocation: path, + artifactLocation: toUri(path), locationMessage: fmt.Sprintf("%v: %v@%v", path, vuln.PkgName, vuln.InstalledVersion), locations: sw.getLocations(vuln.PkgName, vuln.InstalledVersion, path, res.Packages), resultIndex: getRuleIndex(vuln.VulnerabilityID, ruleIndexes), @@ -186,9 +188,9 @@ func (sw *SarifWriter) Write(ctx context.Context, report types.Report) error { vulnerabilityId: misconf.ID, severity: misconf.Severity, cvssScore: severityToScore(misconf.Severity), - url: misconf.PrimaryURL, + url: toUri(misconf.PrimaryURL), resourceClass: res.Class, - artifactLocation: locationURI, + artifactLocation: toUri(locationURI), locationMessage: locationURI, locations: []location{ { @@ -213,9 +215,9 @@ func (sw *SarifWriter) Write(ctx context.Context, report types.Report) error { vulnerabilityId: secret.RuleID, severity: secret.Severity, cvssScore: severityToScore(secret.Severity), - url: builtinRulesUrl, + url: toUri(builtinRulesUrl), resourceClass: res.Class, - artifactLocation: target, + artifactLocation: toUri(target), locationMessage: target, locations: []location{ { @@ -242,9 +244,9 @@ func (sw *SarifWriter) Write(ctx context.Context, report types.Report) error { vulnerabilityId: id, severity: license.Severity, cvssScore: severityToScore(license.Severity), - url: license.Link, + url: toUri(license.Link), resourceClass: res.Class, - artifactLocation: target, + artifactLocation: toUri(target), resultIndex: getRuleIndex(id, ruleIndexes), shortDescription: desc, fullDescription: desc, @@ -348,6 +350,15 @@ func clearURI(s string) string { return strings.ReplaceAll(strings.ReplaceAll(s, "\\", "/"), "git::https:/", "") } +func toUri(str string) *url.URL { + uri, err := url.Parse(str) + if err != nil { + logger := log.WithPrefix("sarif") + logger.Error("Unable to parse URI", log.String("URI", str), log.Err(err)) + } + return uri +} + func (sw *SarifWriter) getLocations(name, version, path string, pkgs []ftypes.Package) []location { id := fmt.Sprintf("%s@%s@%s", path, name, version) locs, ok := sw.locationCache[id] diff --git a/pkg/report/sarif_test.go b/pkg/report/sarif_test.go index cf4bfea8eb0b..ce68fab06a8a 100644 --- a/pkg/report/sarif_test.go +++ b/pkg/report/sarif_test.go @@ -41,7 +41,7 @@ func TestReportWriter_Sarif(t *testing.T) { }, Results: types.Results{ { - Target: "library/test", + Target: "library/test 1", Class: types.ClassOSPkg, Packages: []ftypes.Package{ { @@ -137,10 +137,10 @@ func TestReportWriter_Sarif(t *testing.T) { Message: sarif.Message{Text: lo.ToPtr("Package: foo\nInstalled Version: 1.2.3\nVulnerability CVE-2020-0001\nSeverity: HIGH\nFixed Version: 3.4.5\nLink: [CVE-2020-0001](https://avd.aquasec.com/nvd/cve-2020-0001)")}, Locations: []*sarif.Location{ { - Message: &sarif.Message{Text: lo.ToPtr("library/test: foo@1.2.3")}, + Message: &sarif.Message{Text: lo.ToPtr("library/test 1: foo@1.2.3")}, PhysicalLocation: &sarif.PhysicalLocation{ ArtifactLocation: &sarif.ArtifactLocation{ - URI: lo.ToPtr("library/test"), + URI: lo.ToPtr("library/test%201"), URIBaseId: lo.ToPtr("ROOTPATH"), }, Region: &sarif.Region{ @@ -152,10 +152,10 @@ func TestReportWriter_Sarif(t *testing.T) { }, }, { - Message: &sarif.Message{Text: lo.ToPtr("library/test: foo@1.2.3")}, + Message: &sarif.Message{Text: lo.ToPtr("library/test 1: foo@1.2.3")}, PhysicalLocation: &sarif.PhysicalLocation{ ArtifactLocation: &sarif.ArtifactLocation{ - URI: lo.ToPtr("library/test"), + URI: lo.ToPtr("library/test%201"), URIBaseId: lo.ToPtr("ROOTPATH"), }, Region: &sarif.Region{ @@ -192,7 +192,7 @@ func TestReportWriter_Sarif(t *testing.T) { input: types.Report{ Results: types.Results{ { - Target: "library/test", + Target: "library/test 1", Class: types.ClassConfig, Misconfigurations: []types.DetectedMisconfiguration{ { @@ -283,13 +283,13 @@ func TestReportWriter_Sarif(t *testing.T) { RuleID: lo.ToPtr("KSV001"), RuleIndex: lo.ToPtr[uint](0), Level: lo.ToPtr("error"), - Message: sarif.Message{Text: lo.ToPtr("Artifact: library/test\nType: \nVulnerability KSV001\nSeverity: HIGH\nMessage: Message\nLink: [KSV001](https://avd.aquasec.com/appshield/ksv001)")}, + Message: sarif.Message{Text: lo.ToPtr("Artifact: library/test 1\nType: \nVulnerability KSV001\nSeverity: HIGH\nMessage: Message\nLink: [KSV001](https://avd.aquasec.com/appshield/ksv001)")}, Locations: []*sarif.Location{ { - Message: &sarif.Message{Text: lo.ToPtr("library/test")}, + Message: &sarif.Message{Text: lo.ToPtr("library/test 1")}, PhysicalLocation: &sarif.PhysicalLocation{ ArtifactLocation: &sarif.ArtifactLocation{ - URI: lo.ToPtr("library/test"), + URI: lo.ToPtr("library/test%201"), URIBaseId: lo.ToPtr("ROOTPATH"), }, Region: &sarif.Region{ @@ -306,13 +306,13 @@ func TestReportWriter_Sarif(t *testing.T) { RuleID: lo.ToPtr("KSV002"), RuleIndex: lo.ToPtr[uint](1), Level: lo.ToPtr("error"), - Message: sarif.Message{Text: lo.ToPtr("Artifact: library/test\nType: \nVulnerability KSV002\nSeverity: CRITICAL\nMessage: Message\nLink: [KSV002](https://avd.aquasec.com/appshield/ksv002)")}, + Message: sarif.Message{Text: lo.ToPtr("Artifact: library/test 1\nType: \nVulnerability KSV002\nSeverity: CRITICAL\nMessage: Message\nLink: [KSV002](https://avd.aquasec.com/appshield/ksv002)")}, Locations: []*sarif.Location{ { - Message: &sarif.Message{Text: lo.ToPtr("library/test")}, + Message: &sarif.Message{Text: lo.ToPtr("library/test 1")}, PhysicalLocation: &sarif.PhysicalLocation{ ArtifactLocation: &sarif.ArtifactLocation{ - URI: lo.ToPtr("library/test"), + URI: lo.ToPtr("library/test%201"), URIBaseId: lo.ToPtr("ROOTPATH"), }, Region: &sarif.Region{ @@ -341,7 +341,7 @@ func TestReportWriter_Sarif(t *testing.T) { input: types.Report{ Results: types.Results{ { - Target: "library/test", + Target: "library/test 1", Class: types.ClassSecret, Secrets: []types.DetectedSecret{ { @@ -400,13 +400,13 @@ func TestReportWriter_Sarif(t *testing.T) { RuleID: lo.ToPtr("aws-secret-access-key"), RuleIndex: lo.ToPtr[uint](0), Level: lo.ToPtr("error"), - Message: sarif.Message{Text: lo.ToPtr("Artifact: library/test\nType: \nSecret AWS Secret Access Key\nSeverity: CRITICAL\nMatch: 'AWS_secret_KEY'=\"****************************************\"")}, + Message: sarif.Message{Text: lo.ToPtr("Artifact: library/test 1\nType: \nSecret AWS Secret Access Key\nSeverity: CRITICAL\nMatch: 'AWS_secret_KEY'=\"****************************************\"")}, Locations: []*sarif.Location{ { - Message: &sarif.Message{Text: lo.ToPtr("library/test")}, + Message: &sarif.Message{Text: lo.ToPtr("library/test 1")}, PhysicalLocation: &sarif.PhysicalLocation{ ArtifactLocation: &sarif.ArtifactLocation{ - URI: lo.ToPtr("library/test"), + URI: lo.ToPtr("library/test%201"), URIBaseId: lo.ToPtr("ROOTPATH"), }, Region: &sarif.Region{ @@ -495,7 +495,7 @@ func TestReportWriter_Sarif(t *testing.T) { Message: sarif.NewTextMessage(""), PhysicalLocation: &sarif.PhysicalLocation{ ArtifactLocation: &sarif.ArtifactLocation{ - URI: lo.ToPtr("OS Packages"), + URI: lo.ToPtr("OS%20Packages"), URIBaseId: lo.ToPtr("ROOTPATH"), }, Region: &sarif.Region{ From a585e95f3398631d9ad10505c5ff642fde21aef7 Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Mon, 7 Oct 2024 16:03:40 +0400 Subject: [PATCH 024/108] fix(redhat): include arch in PURL qualifiers (#7654) Signed-off-by: knqyf263 --- pkg/fanal/analyzer/pkg/rpm/archive.go | 9 ++++++++- pkg/fanal/analyzer/pkg/rpm/archive_test.go | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/pkg/fanal/analyzer/pkg/rpm/archive.go b/pkg/fanal/analyzer/pkg/rpm/archive.go index e9db78c6c648..2eda91e4a563 100644 --- a/pkg/fanal/analyzer/pkg/rpm/archive.go +++ b/pkg/fanal/analyzer/pkg/rpm/archive.go @@ -136,7 +136,14 @@ func (a *rpmArchiveAnalyzer) generatePURL(pkg *types.Package) *packageurl.Packag case strings.Contains(vendor, "suse"): ns = "suse" } - return packageurl.NewPackageURL(packageurl.TypeRPM, ns, pkg.Name, utils.FormatVersion(*pkg), nil, "") + var qualifiers packageurl.Qualifiers + if pkg.Arch != "" { + qualifiers = append(qualifiers, packageurl.Qualifier{ + Key: "arch", + Value: pkg.Arch, + }) + } + return packageurl.NewPackageURL(packageurl.TypeRPM, ns, pkg.Name, utils.FormatVersion(*pkg), qualifiers, "") } func (a *rpmArchiveAnalyzer) unexpectedError(err error) error { diff --git a/pkg/fanal/analyzer/pkg/rpm/archive_test.go b/pkg/fanal/analyzer/pkg/rpm/archive_test.go index b9ac0a44ee53..5e8475e171f6 100644 --- a/pkg/fanal/analyzer/pkg/rpm/archive_test.go +++ b/pkg/fanal/analyzer/pkg/rpm/archive_test.go @@ -52,6 +52,12 @@ func Test_rpmArchiveAnalyzer_Analyze(t *testing.T) { Namespace: "redhat", Name: "socat", Version: "1.7.3.2-2.el7", + Qualifiers: packageurl.Qualifiers{ + { + Key: "arch", + Value: "x86_64", + }, + }, }, }, }, From fdf203cd209aeb40f454bd12d121a54d6ed7a542 Mon Sep 17 00:00:00 2001 From: Dan Kirkwood Date: Mon, 7 Oct 2024 22:04:06 -0700 Subject: [PATCH 025/108] fix(repo): `git clone` output to Stderr (#7561) --- pkg/fanal/artifact/repo/git.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/fanal/artifact/repo/git.go b/pkg/fanal/artifact/repo/git.go index 8eb3d9af7f9d..4532b631c6b7 100644 --- a/pkg/fanal/artifact/repo/git.go +++ b/pkg/fanal/artifact/repo/git.go @@ -128,7 +128,7 @@ func cloneRepo(u *url.URL, artifactOpt artifact.Option) (string, error) { cloneOptions := git.CloneOptions{ URL: u.String(), Auth: gitAuth(), - Progress: os.Stdout, + Progress: os.Stderr, InsecureSkipTLS: artifactOpt.Insecure, } From 55b5a7e01b679f58b54b1e84a96da21d940eaf22 Mon Sep 17 00:00:00 2001 From: Pierre Baumard Date: Tue, 8 Oct 2024 19:01:49 +0200 Subject: [PATCH 026/108] docs(report): fix reporting doc format (#7671) --- docs/docs/configuration/reporting.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/docs/configuration/reporting.md b/docs/docs/configuration/reporting.md index fcbde0b106ff..39ecb6e5333c 100644 --- a/docs/docs/configuration/reporting.md +++ b/docs/docs/configuration/reporting.md @@ -259,11 +259,12 @@ $ trivy image --format sarif -o report.sarif golang:1.12-alpine ``` This SARIF file can be uploaded to several platforms, including: -- [GitHub code scanning results][sarif-github], and there is a [Trivy GitHub Action][action] for automating this process; -- [SonarQube][sarif-sonar]. + +- [GitHub code scanning results][sarif-github], and there is a [Trivy GitHub Action][action] for automating this process +- [SonarQube][sarif-sonar] ### GitHub dependency snapshot -Trivy supports the following packages. +Trivy supports the following packages: - [OS packages][os_packages] - [Language-specific packages][language_packages] From 1f2e91b02b3606dd11963002a8cfac7962f3478f Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Wed, 9 Oct 2024 09:53:27 +0400 Subject: [PATCH 027/108] fix(sbom): add options for DBs in private registries (#7660) Signed-off-by: knqyf263 --- docs/docs/references/configuration/cli/trivy_sbom.md | 3 +++ pkg/commands/app.go | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/docs/references/configuration/cli/trivy_sbom.md b/docs/docs/references/configuration/cli/trivy_sbom.md index f09453845518..4eaedcf17aa5 100644 --- a/docs/docs/references/configuration/cli/trivy_sbom.md +++ b/docs/docs/references/configuration/cli/trivy_sbom.md @@ -47,12 +47,14 @@ trivy sbom [flags] SBOM_PATH --offline-scan do not issue API requests to identify dependencies -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments + --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect]) --pkg-types strings list of package types (os,library) (default [os,library]) --redis-ca string redis ca file location, if using redis as cache backend --redis-cert string redis certificate file location, if using redis as cache backend --redis-key string redis key file location, if using redis as cache backend --redis-tls enable redis TLS with public certificates, if using redis as cache backend + --registry-token string registry token --rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev") --sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor) --scanners strings comma-separated list of what security issues to detect (vuln,license) (default [vuln]) @@ -67,6 +69,7 @@ trivy sbom [flags] SBOM_PATH -t, --template string output template --token string for authentication in client/server mode --token-header string specify a header name for token in client/server mode (default "Trivy-Token") + --username strings username. Comma-separated usernames allowed. --vex strings [EXPERIMENTAL] VEX sources ("repo", "oci" or file path) ``` diff --git a/pkg/commands/app.go b/pkg/commands/app.go index 23b44fdec553..bdba2fa12ac7 100644 --- a/pkg/commands/app.go +++ b/pkg/commands/app.go @@ -1143,7 +1143,8 @@ func NewSBOMCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { CacheFlagGroup: flag.NewCacheFlagGroup(), DBFlagGroup: flag.NewDBFlagGroup(), PackageFlagGroup: flag.NewPackageFlagGroup(), - RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode + RemoteFlagGroup: flag.NewClientFlags(), // for client/server mode + RegistryFlagGroup: flag.NewRegistryFlagGroup(), // for DBs in private registries ReportFlagGroup: reportFlagGroup, ScanFlagGroup: scanFlagGroup, VulnerabilityFlagGroup: flag.NewVulnerabilityFlagGroup(), From 27117f81d52483c3ceec56fe56ac298e242fbc9a Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Wed, 9 Oct 2024 14:31:15 +0400 Subject: [PATCH 028/108] feat(cli): add `trivy auth` (#7664) Signed-off-by: knqyf263 Co-authored-by: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> --- contrib/Trivy.gitlab-ci.yml | 2 +- .../docs/advanced/private-registries/index.md | 34 +++-- .../references/configuration/cli/trivy.md | 1 + .../configuration/cli/trivy_auth.md | 29 ++++ .../configuration/cli/trivy_auth_login.md | 41 +++++ .../configuration/cli/trivy_auth_logout.md | 38 +++++ .../configuration/cli/trivy_config.md | 1 + .../configuration/cli/trivy_filesystem.md | 1 + .../configuration/cli/trivy_image.md | 1 + .../configuration/cli/trivy_kubernetes.md | 1 + .../configuration/cli/trivy_repository.md | 1 + .../configuration/cli/trivy_rootfs.md | 1 + .../configuration/cli/trivy_sbom.md | 1 + .../configuration/cli/trivy_server.md | 1 + .../references/configuration/config-file.md | 3 + docs/docs/target/container_image.md | 2 +- go.mod | 2 +- integration/registry_test.go | 39 ++++- mkdocs.yml | 4 + pkg/commands/app.go | 58 +++++++ pkg/commands/auth/run.go | 109 ++++++++++++++ pkg/commands/auth/run_test.go | 142 ++++++++++++++++++ pkg/flag/registry_flags.go | 23 ++- 23 files changed, 512 insertions(+), 23 deletions(-) create mode 100644 docs/docs/references/configuration/cli/trivy_auth.md create mode 100644 docs/docs/references/configuration/cli/trivy_auth_login.md create mode 100644 docs/docs/references/configuration/cli/trivy_auth_logout.md create mode 100644 pkg/commands/auth/run.go create mode 100644 pkg/commands/auth/run_test.go diff --git a/contrib/Trivy.gitlab-ci.yml b/contrib/Trivy.gitlab-ci.yml index 66225b0dac06..a62e26301a20 100644 --- a/contrib/Trivy.gitlab-ci.yml +++ b/contrib/Trivy.gitlab-ci.yml @@ -12,9 +12,9 @@ Trivy_container_scanning: before_script: - export TRIVY_VERSION=${TRIVY_VERSION:-v0.19.2} - apk add --no-cache curl docker-cli - - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY - curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin ${TRIVY_VERSION} - curl -sSL -o /tmp/trivy-gitlab.tpl https://github.com/aquasecurity/trivy/raw/${TRIVY_VERSION}/contrib/gitlab.tpl + - trivy auth login --username "$CI_REGISTRY_USER" --password "$CI_REGISTRY_PASSWORD" $CI_REGISTRY script: - trivy --exit-code 0 --cache-dir .trivycache/ --no-progress --format template --template "@/tmp/trivy-gitlab.tpl" -o gl-container-scanning-report.json $IMAGE cache: diff --git a/docs/docs/advanced/private-registries/index.md b/docs/docs/advanced/private-registries/index.md index 7d5a594cf5b6..7bac7a2742d5 100644 --- a/docs/docs/advanced/private-registries/index.md +++ b/docs/docs/advanced/private-registries/index.md @@ -1,13 +1,30 @@ Trivy can download images from a private registry without the need for installing Docker or any other 3rd party tools. This makes it easy to run within a CI process. -## Credential -To use Trivy with private images, simply install it and provide your credentials: +## Login +You can log in to a private registry using the `trivy auth login` command. +It uses the Docker configuration file (`~/.docker/config.json`) to store the credentials under the hood, and the configuration file path can be configured by `DOCKER_CONFIG` environment variable. + +```shell +$ cat ~/my_password.txt | trivy auth login --username foo --password-stdin ghcr.io +$ trivy image ghcr.io/your/private_image +``` + +## Passing Credentials +You can also provide your credentials when scanning. ```shell $ TRIVY_USERNAME=YOUR_USERNAME TRIVY_PASSWORD=YOUR_PASSWORD trivy image YOUR_PRIVATE_IMAGE ``` +!!! warning + When passing credentials via environment variables or CLI flags, Trivy will attempt to use these credentials for all registries encountered during scanning, regardless of the target registry. + This can potentially lead to unintended credential exposure. + To mitigate this risk: + + 1. Set credentials cautiously and only when necessary. + 2. Prefer using `trivy auth config` to pre-configure credentials with specific registries, which ensures credentials are only sent to appropriate registries. + Trivy also supports providing credentials through CLI flags: ```shell @@ -17,6 +34,7 @@ $ TRIVY_PASSWORD=YOUR_PASSWORD trivy image --username YOUR_USERNAME YOUR_PRIVATE !!! warning The CLI flag `--password` is available, but its use is not recommended for security reasons. + You can also store your credentials in `trivy.yaml`. For more information, please refer to [the documentation](../../references/configuration/config-file.md). @@ -35,15 +53,5 @@ In the example above, Trivy attempts to use two pairs of credentials: Please note that the number of usernames and passwords must be the same. -## docker login -If you have Docker configured locally and have set up the credentials, Trivy can access them. - -```shell -$ docker login ghcr.io -Username: -Password: -$ trivy image ghcr.io/your/private_image -``` - !!! note - `docker login` can be used with any container runtime, such as Podman. + `--password-stdin` doesn't support comma-separated passwords. \ No newline at end of file diff --git a/docs/docs/references/configuration/cli/trivy.md b/docs/docs/references/configuration/cli/trivy.md index 3d8ece9cd0e8..2919e43cbe27 100644 --- a/docs/docs/references/configuration/cli/trivy.md +++ b/docs/docs/references/configuration/cli/trivy.md @@ -43,6 +43,7 @@ trivy [global flags] command [flags] target ### SEE ALSO +* [trivy auth](trivy_auth.md) - Authentication * [trivy clean](trivy_clean.md) - Remove cached files * [trivy config](trivy_config.md) - Scan config files for misconfigurations * [trivy convert](trivy_convert.md) - Convert Trivy JSON report into a different format diff --git a/docs/docs/references/configuration/cli/trivy_auth.md b/docs/docs/references/configuration/cli/trivy_auth.md new file mode 100644 index 000000000000..ba2eaa6a6715 --- /dev/null +++ b/docs/docs/references/configuration/cli/trivy_auth.md @@ -0,0 +1,29 @@ +## trivy auth + +Authentication + +### Options + +``` + -h, --help help for auth +``` + +### Options inherited from parent commands + +``` + --cache-dir string cache directory (default "/path/to/cache") + -c, --config string config path (default "trivy.yaml") + -d, --debug debug mode + --generate-default-config write the default config to trivy-default.yaml + --insecure allow insecure server connections + -q, --quiet suppress progress bar and log output + --timeout duration timeout (default 5m0s) + -v, --version show version +``` + +### SEE ALSO + +* [trivy](trivy.md) - Unified security scanner +* [trivy auth login](trivy_auth_login.md) - Log in to a registry +* [trivy auth logout](trivy_auth_logout.md) - Log out of a registry + diff --git a/docs/docs/references/configuration/cli/trivy_auth_login.md b/docs/docs/references/configuration/cli/trivy_auth_login.md new file mode 100644 index 000000000000..8f2cfdb18abc --- /dev/null +++ b/docs/docs/references/configuration/cli/trivy_auth_login.md @@ -0,0 +1,41 @@ +## trivy auth login + +Log in to a registry + +``` +trivy auth login SERVER [flags] +``` + +### Examples + +``` + # Log in to reg.example.com + cat ~/my_password.txt | trivy auth login --username foo --password-stdin reg.example.com +``` + +### Options + +``` + -h, --help help for login + --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. + --password-stdin password from stdin. Comma-separated passwords are not supported. + --username strings username. Comma-separated usernames allowed. +``` + +### Options inherited from parent commands + +``` + --cache-dir string cache directory (default "/path/to/cache") + -c, --config string config path (default "trivy.yaml") + -d, --debug debug mode + --generate-default-config write the default config to trivy-default.yaml + --insecure allow insecure server connections + -q, --quiet suppress progress bar and log output + --timeout duration timeout (default 5m0s) + -v, --version show version +``` + +### SEE ALSO + +* [trivy auth](trivy_auth.md) - Authentication + diff --git a/docs/docs/references/configuration/cli/trivy_auth_logout.md b/docs/docs/references/configuration/cli/trivy_auth_logout.md new file mode 100644 index 000000000000..3a25958001cd --- /dev/null +++ b/docs/docs/references/configuration/cli/trivy_auth_logout.md @@ -0,0 +1,38 @@ +## trivy auth logout + +Log out of a registry + +``` +trivy auth logout SERVER [flags] +``` + +### Examples + +``` + # Log out of reg.example.com + trivy auth logout reg.example.com +``` + +### Options + +``` + -h, --help help for logout +``` + +### Options inherited from parent commands + +``` + --cache-dir string cache directory (default "/path/to/cache") + -c, --config string config path (default "trivy.yaml") + -d, --debug debug mode + --generate-default-config write the default config to trivy-default.yaml + --insecure allow insecure server connections + -q, --quiet suppress progress bar and log output + --timeout duration timeout (default 5m0s) + -v, --version show version +``` + +### SEE ALSO + +* [trivy auth](trivy_auth.md) - Authentication + diff --git a/docs/docs/references/configuration/cli/trivy_config.md b/docs/docs/references/configuration/cli/trivy_config.md index be7854548496..0c279f1e7bf3 100644 --- a/docs/docs/references/configuration/cli/trivy_config.md +++ b/docs/docs/references/configuration/cli/trivy_config.md @@ -39,6 +39,7 @@ trivy config [flags] DIR -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. + --password-stdin password from stdin. Comma-separated passwords are not supported. --redis-ca string redis ca file location, if using redis as cache backend --redis-cert string redis certificate file location, if using redis as cache backend --redis-key string redis key file location, if using redis as cache backend diff --git a/docs/docs/references/configuration/cli/trivy_filesystem.md b/docs/docs/references/configuration/cli/trivy_filesystem.md index 0abec4990173..bbd5257d5f5d 100644 --- a/docs/docs/references/configuration/cli/trivy_filesystem.md +++ b/docs/docs/references/configuration/cli/trivy_filesystem.md @@ -68,6 +68,7 @@ trivy filesystem [flags] PATH --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. + --password-stdin password from stdin. Comma-separated passwords are not supported. --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect]) --pkg-types strings list of package types (os,library) (default [os,library]) --redis-ca string redis ca file location, if using redis as cache backend diff --git a/docs/docs/references/configuration/cli/trivy_image.md b/docs/docs/references/configuration/cli/trivy_image.md index f9095f041572..779b54f4d5b9 100644 --- a/docs/docs/references/configuration/cli/trivy_image.md +++ b/docs/docs/references/configuration/cli/trivy_image.md @@ -86,6 +86,7 @@ trivy image [flags] IMAGE_NAME --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. + --password-stdin password from stdin. Comma-separated passwords are not supported. --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect]) --pkg-types strings list of package types (os,library) (default [os,library]) --platform string set platform in the form os/arch if image is multi-platform capable diff --git a/docs/docs/references/configuration/cli/trivy_kubernetes.md b/docs/docs/references/configuration/cli/trivy_kubernetes.md index 99fbc1badaa2..6b8058cc00f0 100644 --- a/docs/docs/references/configuration/cli/trivy_kubernetes.md +++ b/docs/docs/references/configuration/cli/trivy_kubernetes.md @@ -83,6 +83,7 @@ trivy kubernetes [flags] [CONTEXT] --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. + --password-stdin password from stdin. Comma-separated passwords are not supported. --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect]) --pkg-types strings list of package types (os,library) (default [os,library]) --qps float specify the maximum QPS to the master from this client (default 5) diff --git a/docs/docs/references/configuration/cli/trivy_repository.md b/docs/docs/references/configuration/cli/trivy_repository.md index 821923262dee..190640f5cb2a 100644 --- a/docs/docs/references/configuration/cli/trivy_repository.md +++ b/docs/docs/references/configuration/cli/trivy_repository.md @@ -68,6 +68,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL) --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. + --password-stdin password from stdin. Comma-separated passwords are not supported. --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect]) --pkg-types strings list of package types (os,library) (default [os,library]) --redis-ca string redis ca file location, if using redis as cache backend diff --git a/docs/docs/references/configuration/cli/trivy_rootfs.md b/docs/docs/references/configuration/cli/trivy_rootfs.md index bf2575af23b9..2f37484d0328 100644 --- a/docs/docs/references/configuration/cli/trivy_rootfs.md +++ b/docs/docs/references/configuration/cli/trivy_rootfs.md @@ -70,6 +70,7 @@ trivy rootfs [flags] ROOTDIR --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --parallel int number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism (default 5) --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. + --password-stdin password from stdin. Comma-separated passwords are not supported. --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect]) --pkg-types strings list of package types (os,library) (default [os,library]) --redis-ca string redis ca file location, if using redis as cache backend diff --git a/docs/docs/references/configuration/cli/trivy_sbom.md b/docs/docs/references/configuration/cli/trivy_sbom.md index 4eaedcf17aa5..55df0959deb6 100644 --- a/docs/docs/references/configuration/cli/trivy_sbom.md +++ b/docs/docs/references/configuration/cli/trivy_sbom.md @@ -48,6 +48,7 @@ trivy sbom [flags] SBOM_PATH -o, --output string output file name --output-plugin-arg string [EXPERIMENTAL] output plugin arguments --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. + --password-stdin password from stdin. Comma-separated passwords are not supported. --pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect]) --pkg-types strings list of package types (os,library) (default [os,library]) --redis-ca string redis ca file location, if using redis as cache backend diff --git a/docs/docs/references/configuration/cli/trivy_server.md b/docs/docs/references/configuration/cli/trivy_server.md index b7ada8f2c19b..80eacd43ee4f 100644 --- a/docs/docs/references/configuration/cli/trivy_server.md +++ b/docs/docs/references/configuration/cli/trivy_server.md @@ -30,6 +30,7 @@ trivy server [flags] --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") --no-progress suppress progress bar --password strings password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons. + --password-stdin password from stdin. Comma-separated passwords are not supported. --redis-ca string redis ca file location, if using redis as cache backend --redis-cert string redis certificate file location, if using redis as cache backend --redis-key string redis key file location, if using redis as cache backend diff --git a/docs/docs/references/configuration/config-file.md b/docs/docs/references/configuration/config-file.md index 9bc9d4351360..ae04978801b3 100644 --- a/docs/docs/references/configuration/config-file.md +++ b/docs/docs/references/configuration/config-file.md @@ -461,6 +461,9 @@ registry: # Same as '--password' password: [] + # Same as '--password-stdin' + password-stdin: false + # Same as '--registry-token' token: "" diff --git a/docs/docs/target/container_image.md b/docs/docs/target/container_image.md index 94acc954a743..6f514db29fb5 100644 --- a/docs/docs/target/container_image.md +++ b/docs/docs/target/container_image.md @@ -297,7 +297,7 @@ Trivy supports registries that comply with the following specifications. - [Docker Registry HTTP API V2](https://docs.docker.com/registry/spec/api/) - [OCI Distribution Specification](https://github.com/opencontainers/distribution-spec) -You can configure credentials with `docker login`. +You can configure credentials with `trivy auth login`. See [here](../advanced/private-registries/index.md) for the detail. ### Tar Files diff --git a/go.mod b/go.mod index d01ce2a7d4c7..1611c8c7080a 100644 --- a/go.mod +++ b/go.mod @@ -43,6 +43,7 @@ require ( github.com/cheggaaa/pb/v3 v3.1.5 github.com/containerd/containerd v1.7.22 github.com/csaf-poc/csaf_distribution/v3 v3.0.0 + github.com/docker/cli v27.2.1+incompatible github.com/docker/docker v27.3.1+incompatible github.com/docker/go-connections v0.5.0 github.com/fatih/color v1.17.0 @@ -210,7 +211,6 @@ require ( github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect - github.com/docker/cli v27.2.1+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.8.2 // indirect github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect diff --git a/integration/registry_test.go b/integration/registry_test.go index 3831ea9a280e..ce77db5ad74a 100644 --- a/integration/registry_test.go +++ b/integration/registry_test.go @@ -117,6 +117,7 @@ type registryOption struct { Username string Password string RegistryToken bool + AuthLogin bool } func TestRegistry(t *testing.T) { @@ -164,7 +165,6 @@ func TestRegistry(t *testing.T) { imageFile: "testdata/fixtures/images/alpine-310.tar.gz", os: "alpine 3.10.2", option: registryOption{ - AuthURL: authURL, Username: authUsername, Password: authPassword, }, @@ -183,13 +183,24 @@ func TestRegistry(t *testing.T) { }, golden: "testdata/alpine-310.json.golden", }, + { + name: "authenticate with 'trivy auth login'", + imageName: "alpine:3.10", + imageFile: "testdata/fixtures/images/alpine-310.tar.gz", + os: "alpine 3.10.2", + option: registryOption{ + Username: authUsername, + Password: authPassword, + AuthLogin: true, + }, + golden: "testdata/alpine-310.json.golden", + }, { name: "amazonlinux 2", imageName: "amazonlinux:2", imageFile: "testdata/fixtures/images/amazon-2.tar.gz", os: "amazon 2 (Karoo)", option: registryOption{ - AuthURL: authURL, Username: authUsername, Password: authPassword, }, @@ -201,7 +212,6 @@ func TestRegistry(t *testing.T) { imageFile: "testdata/fixtures/images/debian-buster.tar.gz", os: "debian 10.1", option: registryOption{ - AuthURL: authURL, Username: authUsername, Password: authPassword, }, @@ -226,6 +236,7 @@ func TestRegistry(t *testing.T) { require.NoError(t, err) osArgs, err := scan(t, imageRef, baseDir, tt.option) + require.NoError(t, err) // Run Trivy runTest(t, osArgs, tt.golden, "", types.FormatJSON, runOptions{ @@ -262,7 +273,7 @@ func scan(t *testing.T, imageRef name.Reference, baseDir string, opt registryOpt "json", "--image-src", "remote", - "--skip-update", + "--skip-db-update", imageRef.Name(), } @@ -273,14 +284,30 @@ func setupEnv(t *testing.T, imageRef name.Reference, baseDir string, opt registr t.Setenv("TRIVY_INSECURE", "true") if opt.Username != "" && opt.Password != "" { - if opt.RegistryToken { + switch { + case opt.RegistryToken: // Get a registry token in advance token, err := requestRegistryToken(imageRef, baseDir, opt) if err != nil { return err } t.Setenv("TRIVY_REGISTRY_TOKEN", token) - } else { + case opt.AuthLogin: + t.Setenv("DOCKER_CONFIG", t.TempDir()) + err := execute([]string{ + "auth", + "login", + "--username", + opt.Username, + "--password", + opt.Password, + "--insecure", + imageRef.Context().RegistryStr(), + }) + if err != nil { + return err + } + default: t.Setenv("TRIVY_USERNAME", opt.Username) t.Setenv("TRIVY_PASSWORD", opt.Password) } diff --git a/mkdocs.yml b/mkdocs.yml index abe494df9dd9..1d3d6276e2c3 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -158,6 +158,10 @@ nav: - Configuration: - CLI: - Overview: docs/references/configuration/cli/trivy.md + - Auth: + - Auth: docs/references/configuration/cli/trivy_auth.md + - Auth Login: docs/references/configuration/cli/trivy_auth_login.md + - Auth Logout: docs/references/configuration/cli/trivy_auth_logout.md - Clean: docs/references/configuration/cli/trivy_clean.md - Config: docs/references/configuration/cli/trivy_config.md - Convert: docs/references/configuration/cli/trivy_convert.md diff --git a/pkg/commands/app.go b/pkg/commands/app.go index bdba2fa12ac7..eec063a3eba3 100644 --- a/pkg/commands/app.go +++ b/pkg/commands/app.go @@ -15,6 +15,7 @@ import ( "github.com/aquasecurity/trivy/pkg/cache" "github.com/aquasecurity/trivy/pkg/commands/artifact" + "github.com/aquasecurity/trivy/pkg/commands/auth" "github.com/aquasecurity/trivy/pkg/commands/clean" "github.com/aquasecurity/trivy/pkg/commands/convert" "github.com/aquasecurity/trivy/pkg/commands/server" @@ -99,6 +100,7 @@ func NewApp() *cobra.Command { NewVersionCommand(globalFlags), NewVMCommand(globalFlags), NewCleanCommand(globalFlags), + NewAuthCommand(globalFlags), NewVEXCommand(globalFlags), ) @@ -1233,6 +1235,62 @@ func NewCleanCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { return cmd } +func NewAuthCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { + cmd := &cobra.Command{ + Use: "auth [flags]", + GroupID: groupUtility, + Short: "Authentication", + SilenceErrors: true, + SilenceUsage: true, + } + + loginFlags := &flag.Flags{ + GlobalFlagGroup: globalFlags, + RegistryFlagGroup: flag.NewRegistryFlagGroup(), + } + loginFlags.RegistryFlagGroup.RegistryToken = nil // disable '--registry-token' + loginCmd := &cobra.Command{ + Use: "login SERVER", + Short: "Log in to a registry", + SilenceErrors: true, + SilenceUsage: true, + Example: ` # Log in to reg.example.com + cat ~/my_password.txt | trivy auth login --username foo --password-stdin reg.example.com`, + Args: cobra.ExactArgs(1), + PreRunE: func(cmd *cobra.Command, args []string) error { + if err := loginFlags.Bind(cmd); err != nil { + return xerrors.Errorf("flag bind error: %w", err) + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + loginOpts, err := loginFlags.ToOptions(args) + if err != nil { + return xerrors.Errorf("flag error: %w", err) + } + return auth.Login(cmd.Context(), args[0], loginOpts) + }, + } + logoutCmd := &cobra.Command{ + Use: "logout SERVER", + Short: "Log out of a registry", + SilenceErrors: true, + SilenceUsage: true, + Example: ` # Log out of reg.example.com + trivy auth logout reg.example.com`, + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + return auth.Logout(cmd.Context(), args[0]) + }, + } + loginFlags.AddFlags(loginCmd) + cmd.AddCommand(loginCmd, logoutCmd) + + cmd.SetFlagErrorFunc(flagErrorFunc) + + return cmd +} + func NewVEXCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { vexFlags := &flag.Flags{ GlobalFlagGroup: globalFlags, diff --git a/pkg/commands/auth/run.go b/pkg/commands/auth/run.go new file mode 100644 index 000000000000..7659702f2b7e --- /dev/null +++ b/pkg/commands/auth/run.go @@ -0,0 +1,109 @@ +package auth + +import ( + "context" + "net/http" + "os" + + "github.com/docker/cli/cli/config" + "github.com/docker/cli/cli/config/types" + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/name" + "github.com/google/go-containerregistry/pkg/v1/remote" + "github.com/google/go-containerregistry/pkg/v1/remote/transport" + "golang.org/x/xerrors" + + "github.com/aquasecurity/trivy/pkg/flag" + "github.com/aquasecurity/trivy/pkg/log" +) + +func Login(ctx context.Context, registry string, opts flag.Options) error { + if len(opts.Credentials) == 0 { + return xerrors.New("username and password required") + } else if len(opts.Credentials) > 1 { + return xerrors.New("multiple credentials are not allowed") + } + + reg, err := parseRegistry(registry, opts) + if err != nil { + return xerrors.Errorf("failed to parse registry: %w", err) + } + serverAddress := reg.Name() + + // Validate the credential + _, err = transport.NewWithContext(ctx, reg, &authn.Basic{ + Username: opts.Credentials[0].Username, + Password: opts.Credentials[0].Password, + }, httpTransport(opts), []string{reg.Scope(transport.PullScope)}) + if err != nil { + return xerrors.Errorf("failed to authenticate: %w", err) + } + + cf, err := config.Load(os.Getenv("DOCKER_CONFIG")) + if err != nil { + return xerrors.Errorf("failed to load docker config: %w", err) + } + creds := cf.GetCredentialsStore(serverAddress) + if serverAddress == name.DefaultRegistry { + serverAddress = authn.DefaultAuthKey + } + if err := creds.Store(types.AuthConfig{ + ServerAddress: serverAddress, + Username: opts.Credentials[0].Username, + Password: opts.Credentials[0].Password, + }); err != nil { + return xerrors.Errorf("failed to store credentials: %w", err) + } + + if err := cf.Save(); err != nil { + return xerrors.Errorf("failed to save docker config: %w", err) + } + log.Info("Login succeeded", log.FilePath(cf.Filename), log.String("username", opts.Credentials[0].Username)) + return nil +} + +func Logout(_ context.Context, registry string) error { + reg, err := parseRegistry(registry, flag.Options{}) + if err != nil { + return xerrors.Errorf("failed to parse registry: %w", err) + } + serverAddress := reg.Name() + + cf, err := config.Load(os.Getenv("DOCKER_CONFIG")) + if err != nil { + return xerrors.Errorf("failed to load docker config: %w", err) + } + creds := cf.GetCredentialsStore(serverAddress) + if serverAddress == name.DefaultRegistry { + serverAddress = authn.DefaultAuthKey + } + if err := creds.Erase(serverAddress); err != nil { + return xerrors.Errorf("failed to delete credentials: %w", err) + } + + if err := cf.Save(); err != nil { + return xerrors.Errorf("failed to save docker config: %w", err) + } + log.Info("Logged out", log.FilePath(cf.Filename)) + return nil +} + +func parseRegistry(registry string, opts flag.Options) (name.Registry, error) { + var nameOpts []name.Option + if opts.Insecure { + nameOpts = append(nameOpts, name.Insecure) + } + reg, err := name.NewRegistry(registry, nameOpts...) + if err != nil { + return name.Registry{}, xerrors.Errorf("failed to parse registry: %w", err) + } + return reg, nil +} + +func httpTransport(opts flag.Options) *http.Transport { + tr := remote.DefaultTransport.(*http.Transport).Clone() + if opts.Insecure { + tr.TLSClientConfig.InsecureSkipVerify = true + } + return tr +} diff --git a/pkg/commands/auth/run_test.go b/pkg/commands/auth/run_test.go new file mode 100644 index 000000000000..2b2df244031f --- /dev/null +++ b/pkg/commands/auth/run_test.go @@ -0,0 +1,142 @@ +package auth_test + +import ( + "context" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/samber/lo" + "github.com/stretchr/testify/require" + + testauth "github.com/aquasecurity/testdocker/auth" + "github.com/aquasecurity/testdocker/registry" + "github.com/aquasecurity/trivy/pkg/commands/auth" + "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/flag" +) + +func TestLogin(t *testing.T) { + type args struct { + registry string + opts flag.Options + } + tests := []struct { + name string + args args + wantErr string + }{ + { + name: "single credential", + args: args{ + opts: flag.Options{ + RegistryOptions: flag.RegistryOptions{ + Credentials: []types.Credential{ + { + Username: "user", + Password: "pass", + }, + }, + }, + }, + }, + }, + { + name: "multiple credentials", + args: args{ + opts: flag.Options{ + RegistryOptions: flag.RegistryOptions{ + Credentials: []types.Credential{ + { + Username: "user1", + Password: "pass1", + }, + { + Username: "user2", + Password: "pass2", + }, + }, + }, + }, + }, + wantErr: "multiple credentials are not allowed", + }, + { + name: "no credentials", + args: args{ + registry: "auth.test", + opts: flag.Options{}, + }, + wantErr: "username and password required", + }, + { + name: "invalid registry", + args: args{ + registry: "aaa://invalid.test", + opts: flag.Options{ + RegistryOptions: flag.RegistryOptions{ + Credentials: []types.Credential{ + { + Username: "user", + Password: "pass", + }, + }, + }, + }, + }, + wantErr: "registries must be valid RFC 3986 URI authorities", + }, + } + + tr := registry.NewDockerRegistry(registry.Option{ + Auth: testauth.Auth{ + User: "user", + Password: "pass", + }, + }) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Set the DOCKER_CONFIG environment variable to a temporary directory + // so that the test does not interfere with the user's configuration. + t.Setenv("DOCKER_CONFIG", filepath.Join(t.TempDir(), "config.json")) + + reg := lo.Ternary(tt.args.registry == "", strings.TrimPrefix(tr.URL, "http://"), tt.args.registry) + err := auth.Login(context.Background(), reg, tt.args.opts) + if tt.wantErr != "" { + require.ErrorContains(t, err, tt.wantErr) + return + } + require.NoError(t, err) + }) + } +} + +func TestLogout(t *testing.T) { + // Set the DOCKER_CONFIG environment variable to a temporary directory + // so that the test does not interfere with the user's configuration. + tmpDir := t.TempDir() + t.Setenv("DOCKER_CONFIG", tmpDir) + + t.Run("success", func(t *testing.T) { + configFile := filepath.Join(tmpDir, "config.json") + err := os.WriteFile(configFile, []byte(`{"auths": {"auth.test": {"auth": "dXNlcjpwYXNz"}}}`), 0600) + require.NoError(t, err) + + err = auth.Logout(context.Background(), "auth.test") + require.NoError(t, err) + b, err := os.ReadFile(configFile) + require.NoError(t, err) + require.JSONEq(t, `{"auths": {}}`, string(b)) + }) + t.Run("not found", func(t *testing.T) { + err := auth.Logout(context.Background(), "notfound.test") + require.NoError(t, err) // Return an error if "credsStore" is "osxkeychain". + }) + + t.Run("invalid registry", func(t *testing.T) { + err := auth.Logout(context.Background(), "aaa://invalid.test") + require.ErrorContains(t, err, "registries must be valid RFC 3986 URI authorities") + }) +} diff --git a/pkg/flag/registry_flags.go b/pkg/flag/registry_flags.go index 552eaf4276d6..9c03a07b8872 100644 --- a/pkg/flag/registry_flags.go +++ b/pkg/flag/registry_flags.go @@ -1,6 +1,8 @@ package flag import ( + "io" + "os" "strings" "golang.org/x/xerrors" @@ -19,6 +21,11 @@ var ( ConfigName: "registry.password", Usage: "password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.", } + PasswordStdinFlag = Flag[bool]{ + Name: "password-stdin", + ConfigName: "registry.password-stdin", + Usage: "password from stdin. Comma-separated passwords are not supported.", + } RegistryTokenFlag = Flag[string]{ Name: "registry-token", ConfigName: "registry.token", @@ -29,6 +36,7 @@ var ( type RegistryFlagGroup struct { Username *Flag[[]string] Password *Flag[[]string] + PasswordStdin *Flag[bool] RegistryToken *Flag[string] } @@ -41,6 +49,7 @@ func NewRegistryFlagGroup() *RegistryFlagGroup { return &RegistryFlagGroup{ Username: UsernameFlag.Clone(), Password: PasswordFlag.Clone(), + PasswordStdin: PasswordStdinFlag.Clone(), RegistryToken: RegistryTokenFlag.Clone(), } } @@ -53,6 +62,7 @@ func (f *RegistryFlagGroup) Flags() []Flagger { return []Flagger{ f.Username, f.Password, + f.PasswordStdin, f.RegistryToken, } } @@ -65,8 +75,19 @@ func (f *RegistryFlagGroup) ToOptions() (RegistryOptions, error) { var credentials []types.Credential users := f.Username.Value() passwords := f.Password.Value() + if f.PasswordStdin.Value() { + if len(passwords) != 0 { + return RegistryOptions{}, xerrors.New("'--password' and '--password-stdin' can't be used at the same time") + } + contents, err := io.ReadAll(os.Stdin) + if err != nil { + return RegistryOptions{}, xerrors.Errorf("failed to read from stdin: %w", err) + } + // "--password-stdin" doesn't support comma-separated passwords + passwords = []string{strings.TrimRight(string(contents), "\r\n")} + } if len(users) != len(passwords) { - return RegistryOptions{}, xerrors.New("the length of usernames and passwords must match") + return RegistryOptions{}, xerrors.New("the number of usernames and passwords must match") } for i, user := range users { credentials = append(credentials, types.Credential{ From 672e886aed152ae0f09a16941706746f3053ca94 Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Thu, 10 Oct 2024 15:02:06 +0400 Subject: [PATCH 029/108] fix(cli): `clean --all` deletes only relevant dirs (#7704) Signed-off-by: knqyf263 --- pkg/commands/clean/run.go | 15 +++++---------- pkg/commands/clean/run_test.go | 26 +++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/pkg/commands/clean/run.go b/pkg/commands/clean/run.go index c60227360d19..8dd83519422f 100644 --- a/pkg/commands/clean/run.go +++ b/pkg/commands/clean/run.go @@ -2,7 +2,6 @@ package clean import ( "context" - "os" "golang.org/x/xerrors" @@ -25,7 +24,11 @@ func Run(ctx context.Context, opts flag.Options) error { } if opts.CleanAll { - return cleanAll(ctx, opts) + opts.CleanScanCache = true + opts.CleanVulnerabilityDB = true + opts.CleanJavaDB = true + opts.CleanChecksBundle = true + opts.CleanVEXRepositories = true } if opts.CleanScanCache { @@ -60,14 +63,6 @@ func Run(ctx context.Context, opts flag.Options) error { return nil } -func cleanAll(ctx context.Context, opts flag.Options) error { - log.InfoContext(ctx, "Removing all caches...") - if err := os.RemoveAll(opts.CacheDir); err != nil { - return xerrors.Errorf("failed to remove the directory (%s) : %w", opts.CacheDir, err) - } - return nil -} - func cleanScanCache(ctx context.Context, opts flag.Options) error { log.InfoContext(ctx, "Removing scan cache...") c, cleanup, err := cache.New(opts.CacheOpts()) diff --git a/pkg/commands/clean/run_test.go b/pkg/commands/clean/run_test.go index 9b301d238219..32e4110aba97 100644 --- a/pkg/commands/clean/run_test.go +++ b/pkg/commands/clean/run_test.go @@ -28,7 +28,12 @@ func TestRun(t *testing.T) { }, wantErr: false, checkFunc: func(t *testing.T, dir string) { - assert.NoDirExists(t, dir) + assert.NoDirExists(t, filepath.Join(dir, "fanal")) + assert.NoDirExists(t, filepath.Join(dir, "db")) + assert.NoDirExists(t, filepath.Join(dir, "java-db")) + assert.NoDirExists(t, filepath.Join(dir, "policy")) + assert.NoDirExists(t, filepath.Join(dir, "vex")) + assert.DirExists(t, dir) }, }, { @@ -42,6 +47,7 @@ func TestRun(t *testing.T) { assert.DirExists(t, filepath.Join(dir, "db")) assert.DirExists(t, filepath.Join(dir, "java-db")) assert.DirExists(t, filepath.Join(dir, "policy")) + assert.DirExists(t, filepath.Join(dir, "vex")) }, }, { @@ -55,6 +61,7 @@ func TestRun(t *testing.T) { assert.DirExists(t, filepath.Join(dir, "fanal")) assert.DirExists(t, filepath.Join(dir, "java-db")) assert.DirExists(t, filepath.Join(dir, "policy")) + assert.DirExists(t, filepath.Join(dir, "vex")) }, }, { @@ -68,6 +75,7 @@ func TestRun(t *testing.T) { assert.DirExists(t, filepath.Join(dir, "fanal")) assert.DirExists(t, filepath.Join(dir, "db")) assert.DirExists(t, filepath.Join(dir, "policy")) + assert.DirExists(t, filepath.Join(dir, "vex")) }, }, { @@ -81,6 +89,21 @@ func TestRun(t *testing.T) { assert.DirExists(t, filepath.Join(dir, "fanal")) assert.DirExists(t, filepath.Join(dir, "db")) assert.DirExists(t, filepath.Join(dir, "java-db")) + assert.DirExists(t, filepath.Join(dir, "vex")) + }, + }, + { + name: "clean vex repositories", + cleanOpts: flag.CleanOptions{ + CleanVEXRepositories: true, + }, + wantErr: false, + checkFunc: func(t *testing.T, dir string) { + assert.DirExists(t, filepath.Join(dir, "policy")) + assert.DirExists(t, filepath.Join(dir, "fanal")) + assert.DirExists(t, filepath.Join(dir, "db")) + assert.DirExists(t, filepath.Join(dir, "java-db")) + assert.NoDirExists(t, filepath.Join(dir, "vex")) }, }, { @@ -127,6 +150,7 @@ func createTestFiles(t *testing.T, dir string) { "db", "java-db", "policy", + "vex", } for _, subdir := range subdirs { err := os.MkdirAll(filepath.Join(dir, subdir), 0755) From c78f45b4a71a0fb92232aad7594351466125bee2 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Fri, 11 Oct 2024 10:40:13 +0600 Subject: [PATCH 030/108] refactor(misconf): introduce generic scanner (#7515) Signed-off-by: nikpivkin --- pkg/iac/scanners/dockerfile/parser/parser.go | 64 +---- .../scanners/dockerfile/parser/parser_test.go | 13 +- pkg/iac/scanners/dockerfile/scanner.go | 108 +-------- pkg/iac/scanners/dockerfile/scanner_test.go | 7 +- pkg/iac/scanners/generic/scanner.go | 220 +++++++++++++++++ pkg/iac/scanners/generic/scanner_test.go | 223 ++++++++++++++++++ pkg/iac/scanners/json/parser/parser.go | 65 ----- pkg/iac/scanners/json/parser/parser_test.go | 51 ---- pkg/iac/scanners/json/scanner.go | 113 --------- pkg/iac/scanners/json/scanner_test.go | 80 ------- pkg/iac/scanners/toml/parser/parser.go | 65 ----- pkg/iac/scanners/toml/parser/parser_test.go | 55 ----- pkg/iac/scanners/toml/scanner.go | 111 --------- pkg/iac/scanners/toml/scanner_test.go | 85 ------- pkg/iac/scanners/yaml/parser/parser.go | 86 ------- pkg/iac/scanners/yaml/parser/parser_test.go | 150 ------------ pkg/iac/scanners/yaml/scanner.go | 113 --------- pkg/iac/scanners/yaml/scanner_test.go | 88 ------- pkg/misconf/scanner.go | 7 +- 19 files changed, 469 insertions(+), 1235 deletions(-) create mode 100644 pkg/iac/scanners/generic/scanner.go create mode 100644 pkg/iac/scanners/generic/scanner_test.go delete mode 100644 pkg/iac/scanners/json/parser/parser.go delete mode 100644 pkg/iac/scanners/json/parser/parser_test.go delete mode 100644 pkg/iac/scanners/json/scanner.go delete mode 100644 pkg/iac/scanners/json/scanner_test.go delete mode 100644 pkg/iac/scanners/toml/parser/parser.go delete mode 100644 pkg/iac/scanners/toml/parser/parser_test.go delete mode 100644 pkg/iac/scanners/toml/scanner.go delete mode 100644 pkg/iac/scanners/toml/scanner_test.go delete mode 100644 pkg/iac/scanners/yaml/parser/parser.go delete mode 100644 pkg/iac/scanners/yaml/parser/parser_test.go delete mode 100644 pkg/iac/scanners/yaml/scanner.go delete mode 100644 pkg/iac/scanners/yaml/scanner_test.go diff --git a/pkg/iac/scanners/dockerfile/parser/parser.go b/pkg/iac/scanners/dockerfile/parser/parser.go index e92116ae3079..128d9515e8b1 100644 --- a/pkg/iac/scanners/dockerfile/parser/parser.go +++ b/pkg/iac/scanners/dockerfile/parser/parser.go @@ -4,76 +4,26 @@ import ( "context" "fmt" "io" - "io/fs" - "path/filepath" "strings" "github.com/moby/buildkit/frontend/dockerfile/instructions" "github.com/moby/buildkit/frontend/dockerfile/parser" "github.com/aquasecurity/trivy/pkg/iac/providers/dockerfile" - "github.com/aquasecurity/trivy/pkg/log" ) -type Parser struct { - logger *log.Logger -} - -// New creates a new Dockerfile parser -func New() *Parser { - return &Parser{ - logger: log.WithPrefix("dockerfile parser"), - } -} - -func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) (map[string]*dockerfile.Dockerfile, error) { - - files := make(map[string]*dockerfile.Dockerfile) - if err := fs.WalkDir(target, filepath.ToSlash(path), func(path string, entry fs.DirEntry, err error) error { - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - if err != nil { - return err - } - if entry.IsDir() { - return nil - } - - df, err := p.ParseFile(ctx, target, path) - if err != nil { - p.logger.Error("Failed to parse Dockerfile", log.FilePath(path), log.Err(err)) - return nil - } - files[path] = df - return nil - }); err != nil { - return nil, err - } - return files, nil -} - -// ParseFile parses Dockerfile content from the provided filesystem path. -func (p *Parser) ParseFile(_ context.Context, fsys fs.FS, path string) (*dockerfile.Dockerfile, error) { - f, err := fsys.Open(filepath.ToSlash(path)) - if err != nil { - return nil, err - } - defer func() { _ = f.Close() }() - return p.parse(path, f) -} - -func (p *Parser) parse(path string, r io.Reader) (*dockerfile.Dockerfile, error) { +func Parse(_ context.Context, r io.Reader, path string) (any, error) { parsed, err := parser.Parse(r) if err != nil { return nil, fmt.Errorf("dockerfile parse error: %w", err) } - var parsedFile dockerfile.Dockerfile - var stage dockerfile.Stage - var stageIndex int + var ( + parsedFile dockerfile.Dockerfile + stage dockerfile.Stage + stageIndex int + ) + fromValue := "args" for _, child := range parsed.AST.Children { child.Value = strings.ToLower(child.Value) diff --git a/pkg/iac/scanners/dockerfile/parser/parser_test.go b/pkg/iac/scanners/dockerfile/parser/parser_test.go index 764cb3a0e091..ad1acf193bf3 100644 --- a/pkg/iac/scanners/dockerfile/parser/parser_test.go +++ b/pkg/iac/scanners/dockerfile/parser/parser_test.go @@ -1,11 +1,15 @@ -package parser +package parser_test import ( + "context" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/aquasecurity/trivy/pkg/iac/providers/dockerfile" + "github.com/aquasecurity/trivy/pkg/iac/scanners/dockerfile/parser" ) func Test_Parser(t *testing.T) { @@ -15,12 +19,13 @@ RUN make /app CMD python /app/app.py ` - df, err := New().parse("Dockerfile", strings.NewReader(input)) + res, err := parser.Parse(context.TODO(), strings.NewReader(input), "Dockerfile") require.NoError(t, err) - assert.Len(t, df.Stages, 1) + df, ok := res.(*dockerfile.Dockerfile) + require.True(t, ok) - require.Len(t, df.Stages, 1) + assert.Len(t, df.Stages, 1) assert.Equal(t, "ubuntu:18.04", df.Stages[0].Name) commands := df.Stages[0].Commands diff --git a/pkg/iac/scanners/dockerfile/scanner.go b/pkg/iac/scanners/dockerfile/scanner.go index 1694f28d814a..5637410945eb 100644 --- a/pkg/iac/scanners/dockerfile/scanner.go +++ b/pkg/iac/scanners/dockerfile/scanner.go @@ -1,114 +1,12 @@ package dockerfile import ( - "context" - "io/fs" - "sync" - - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners" "github.com/aquasecurity/trivy/pkg/iac/scanners/dockerfile/parser" + "github.com/aquasecurity/trivy/pkg/iac/scanners/generic" "github.com/aquasecurity/trivy/pkg/iac/scanners/options" "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/log" ) -var _ scanners.FSScanner = (*Scanner)(nil) -var _ options.ConfigurableScanner = (*Scanner)(nil) - -type Scanner struct { - mu sync.Mutex - logger *log.Logger - parser *parser.Parser - regoScanner *rego.Scanner - options []options.ScannerOption -} - -func (s *Scanner) SetIncludeDeprecatedChecks(bool) {} -func (s *Scanner) SetRegoOnly(bool) {} -func (s *Scanner) SetFrameworks(frameworks []framework.Framework) {} - -func (s *Scanner) Name() string { - return "Dockerfile" -} - -func NewScanner(opts ...options.ScannerOption) *Scanner { - s := &Scanner{ - options: opts, - logger: log.WithPrefix("dockerfile scanner"), - } - for _, opt := range opts { - opt(s) - } - s.parser = parser.New() - return s -} - -func (s *Scanner) ScanFS(ctx context.Context, fsys fs.FS, path string) (scan.Results, error) { - - files, err := s.parser.ParseFS(ctx, fsys, path) - if err != nil { - return nil, err - } - - if len(files) == 0 { - return nil, nil - } - - var inputs []rego.Input - for path, dfile := range files { - inputs = append(inputs, rego.Input{ - Path: path, - FS: fsys, - Contents: dfile.ToRego(), - }) - } - - results, err := s.scanRego(ctx, fsys, inputs...) - if err != nil { - return nil, err - } - return results, nil -} - -func (s *Scanner) ScanFile(ctx context.Context, fsys fs.FS, path string) (scan.Results, error) { - dockerfile, err := s.parser.ParseFile(ctx, fsys, path) - if err != nil { - return nil, err - } - s.logger.Debug("Scanning", log.FilePath(path)) - return s.scanRego(ctx, fsys, rego.Input{ - Path: path, - Contents: dockerfile.ToRego(), - }) -} - -func (s *Scanner) initRegoScanner(srcFS fs.FS) (*rego.Scanner, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.regoScanner != nil { - return s.regoScanner, nil - } - - regoScanner := rego.NewScanner(types.SourceDockerfile, s.options...) - if err := regoScanner.LoadPolicies(srcFS); err != nil { - return nil, err - } - s.regoScanner = regoScanner - return regoScanner, nil -} - -func (s *Scanner) scanRego(ctx context.Context, srcFS fs.FS, inputs ...rego.Input) (scan.Results, error) { - regoScanner, err := s.initRegoScanner(srcFS) - if err != nil { - return nil, err - } - results, err := regoScanner.ScanInput(ctx, inputs...) - if err != nil { - return nil, err - } - results.SetSourceAndFilesystem("", srcFS, false) - return results, nil +func NewScanner(opts ...options.ScannerOption) *generic.GenericScanner { + return generic.NewScanner("Dockerfile", types.SourceDockerfile, generic.ParseFunc(parser.Parse), opts...) } diff --git a/pkg/iac/scanners/dockerfile/scanner_test.go b/pkg/iac/scanners/dockerfile/scanner_test.go index 2872df1e4ef6..e8c66b27db08 100644 --- a/pkg/iac/scanners/dockerfile/scanner_test.go +++ b/pkg/iac/scanners/dockerfile/scanner_test.go @@ -1,4 +1,4 @@ -package dockerfile +package dockerfile_test import ( "bytes" @@ -13,6 +13,7 @@ import ( "github.com/aquasecurity/trivy/pkg/iac/rego" "github.com/aquasecurity/trivy/pkg/iac/rego/schemas" "github.com/aquasecurity/trivy/pkg/iac/scan" + "github.com/aquasecurity/trivy/pkg/iac/scanners/dockerfile" ) const DS006PolicyWithDockerfileSchema = `# METADATA @@ -219,7 +220,7 @@ USER root "/rules/rule.rego": DS006LegacyWithOldStyleMetadata, }) - scanner := NewScanner(rego.WithPolicyDirs("rules")) + scanner := dockerfile.NewScanner(rego.WithPolicyDirs("rules")) results, err := scanner.ScanFS(context.TODO(), fs, "code") require.NoError(t, err) @@ -563,7 +564,7 @@ COPY --from=dep /binary /` var traceBuf bytes.Buffer - scanner := NewScanner( + scanner := dockerfile.NewScanner( rego.WithPolicyDirs("rules"), rego.WithEmbeddedLibraries(true), rego.WithTrace(&traceBuf), diff --git a/pkg/iac/scanners/generic/scanner.go b/pkg/iac/scanners/generic/scanner.go new file mode 100644 index 000000000000..5a36709d9a04 --- /dev/null +++ b/pkg/iac/scanners/generic/scanner.go @@ -0,0 +1,220 @@ +package generic + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "io/fs" + "path/filepath" + "strings" + "sync" + + "github.com/BurntSushi/toml" + "gopkg.in/yaml.v3" + + "github.com/aquasecurity/trivy/pkg/iac/framework" + "github.com/aquasecurity/trivy/pkg/iac/rego" + "github.com/aquasecurity/trivy/pkg/iac/scan" + "github.com/aquasecurity/trivy/pkg/iac/scanners/options" + "github.com/aquasecurity/trivy/pkg/iac/types" + "github.com/aquasecurity/trivy/pkg/log" +) + +func NewJsonScanner(opts ...options.ScannerOption) *GenericScanner { + return NewScanner("JSON", types.SourceJSON, ParseFunc(parseJson), opts...) +} + +func NewYamlScanner(opts ...options.ScannerOption) *GenericScanner { + return NewScanner("YAML", types.SourceYAML, ParseFunc(parseYaml), opts...) +} + +func NewTomlScanner(opts ...options.ScannerOption) *GenericScanner { + return NewScanner("TOML", types.SourceTOML, ParseFunc(parseTOML), opts...) +} + +type configParser interface { + Parse(ctx context.Context, r io.Reader, path string) (any, error) +} + +// GenericScanner is a scanner that scans a file as is without processing it +type GenericScanner struct { + mu sync.Mutex + name string + source types.Source + logger *log.Logger + options []options.ScannerOption + regoScanner *rego.Scanner + + parser configParser +} + +type ParseFunc func(ctx context.Context, r io.Reader, path string) (any, error) + +func (f ParseFunc) Parse(ctx context.Context, r io.Reader, path string) (any, error) { + return f(ctx, r, path) +} + +func NewScanner(name string, source types.Source, parser configParser, opts ...options.ScannerOption) *GenericScanner { + s := &GenericScanner{ + name: name, + options: opts, + source: source, + logger: log.WithPrefix(fmt.Sprintf("%s scanner", source)), + parser: parser, + } + + for _, opt := range opts { + opt(s) + } + + return s +} + +func (s *GenericScanner) Name() string { + return s.name +} + +func (s *GenericScanner) ScanFS(ctx context.Context, fsys fs.FS, dir string) (scan.Results, error) { + fileset, err := s.parseFS(ctx, fsys, dir) + if err != nil { + return nil, err + } + + if len(fileset) == 0 { + return nil, nil + } + + var inputs []rego.Input + for path, val := range fileset { + switch v := val.(type) { + case interface{ ToRego() any }: + inputs = append(inputs, rego.Input{ + Path: path, + Contents: v.ToRego(), + FS: fsys, + }) + case []any: + for _, file := range v { + inputs = append(inputs, rego.Input{ + Path: path, + Contents: file, + FS: fsys, + }) + } + default: + inputs = append(inputs, rego.Input{ + Path: path, + Contents: v, + FS: fsys, + }) + } + } + + regoScanner, err := s.initRegoScanner(fsys) + if err != nil { + return nil, err + } + + s.logger.Debug("Scanning files...", log.Int("count", len(inputs))) + results, err := regoScanner.ScanInput(ctx, inputs...) + if err != nil { + return nil, err + } + results.SetSourceAndFilesystem("", fsys, false) + return results, nil +} + +func (s *GenericScanner) parseFS(ctx context.Context, fsys fs.FS, path string) (map[string]any, error) { + files := make(map[string]any) + if err := fs.WalkDir(fsys, filepath.ToSlash(path), func(path string, entry fs.DirEntry, err error) error { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + if err != nil { + return err + } + if entry.IsDir() { + return nil + } + + f, err := fsys.Open(filepath.ToSlash(path)) + if err != nil { + return err + } + defer f.Close() + + df, err := s.parser.Parse(ctx, f, path) + if err != nil { + s.logger.Error("Failed to parse file", log.FilePath(path), log.Err(err)) + return nil + } + files[path] = df + return nil + }); err != nil { + return nil, err + } + return files, nil +} + +func (s *GenericScanner) initRegoScanner(srcFS fs.FS) (*rego.Scanner, error) { + s.mu.Lock() + defer s.mu.Unlock() + if s.regoScanner != nil { + return s.regoScanner, nil + } + regoScanner := rego.NewScanner(s.source, s.options...) + if err := regoScanner.LoadPolicies(srcFS); err != nil { + return nil, err + } + s.regoScanner = regoScanner + return regoScanner, nil +} + +func (*GenericScanner) SetRegoOnly(bool) {} +func (*GenericScanner) SetIncludeDeprecatedChecks(bool) {} +func (*GenericScanner) SetFrameworks([]framework.Framework) {} + +func parseJson(ctx context.Context, r io.Reader, _ string) (any, error) { + var target any + if err := json.NewDecoder(r).Decode(&target); err != nil { + return nil, err + } + return target, nil +} + +func parseYaml(ctx context.Context, r io.Reader, _ string) (any, error) { + contents, err := io.ReadAll(r) + if err != nil { + return nil, err + } + + var results []any + + marker := "\n---\n" + altMarker := "\r\n---\r\n" + if bytes.Contains(contents, []byte(altMarker)) { + marker = altMarker + } + + for _, partial := range strings.Split(string(contents), marker) { + var target any + if err := yaml.Unmarshal([]byte(partial), &target); err != nil { + return nil, err + } + results = append(results, target) + } + + return results, nil +} + +func parseTOML(ctx context.Context, r io.Reader, _ string) (any, error) { + var target any + if _, err := toml.NewDecoder(r).Decode(&target); err != nil { + return nil, err + } + return target, nil +} diff --git a/pkg/iac/scanners/generic/scanner_test.go b/pkg/iac/scanners/generic/scanner_test.go new file mode 100644 index 000000000000..46d0eef1653a --- /dev/null +++ b/pkg/iac/scanners/generic/scanner_test.go @@ -0,0 +1,223 @@ +package generic_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/aquasecurity/trivy/internal/testutil" + "github.com/aquasecurity/trivy/pkg/iac/framework" + "github.com/aquasecurity/trivy/pkg/iac/rego" + "github.com/aquasecurity/trivy/pkg/iac/scan" + "github.com/aquasecurity/trivy/pkg/iac/scanners/generic" +) + +func TestJsonScanner(t *testing.T) { + fsys := testutil.CreateFS(t, map[string]string{ + "/code/data.json": `{ "x": { "y": 123, "z": ["a", "b", "c"]}}`, + "/rules/rule.rego": `package builtin.json.lol + +__rego_metadata__ := { + "id": "ABC123", + "avd_id": "AVD-AB-0123", + "title": "title", + "short_code": "short", + "severity": "CRITICAL", + "type": "JSON Check", + "description": "description", + "recommended_actions": "actions", + "url": "https://example.com", +} + +__rego_input__ := { + "combine": false, + "selector": [{"type": "json"}], +} + +deny[res] { + input.x.y == 123 + res := { + "msg": "oh no", + "startline": 1, + "endline": 2, + } +} + +`, + }) + + scanner := generic.NewJsonScanner(rego.WithPolicyDirs("rules")) + + results, err := scanner.ScanFS(context.TODO(), fsys, "code") + require.NoError(t, err) + + require.Len(t, results.GetFailed(), 1) + + assert.Equal(t, scan.Rule{ + AVDID: "AVD-AB-0123", + Aliases: []string{"ABC123"}, + ShortCode: "short", + Summary: "title", + Explanation: "description", + Impact: "", + Resolution: "actions", + Provider: "json", + Service: "general", + Links: []string{"https://example.com"}, + Severity: "CRITICAL", + Terraform: &scan.EngineMetadata{}, + CloudFormation: &scan.EngineMetadata{}, + CustomChecks: scan.CustomChecks{ + Terraform: (*scan.TerraformCustomCheck)(nil), + }, + RegoPackage: "data.builtin.json.lol", + Frameworks: map[framework.Framework][]string{ + framework.Default: {}, + }, + }, results.GetFailed()[0].Rule()) +} + +func TestYamlScanner(t *testing.T) { + fsys := testutil.CreateFS(t, map[string]string{ + "/code/data.yaml": `--- +x: + y: 123 + z: + - a + - b + - c +`, + "/rules/rule.rego": `package builtin.yaml.lol + +__rego_metadata__ := { + "id": "ABC123", + "avd_id": "AVD-AB-0123", + "title": "title", + "short_code": "short", + "severity": "CRITICAL", + "type": "YAML Check", + "description": "description", + "recommended_actions": "actions", + "url": "https://example.com", +} + +__rego_input__ := { + "combine": false, + "selector": [{"type": "yaml"}], +} + +deny[res] { + input.x.y == 123 + res := { + "msg": "oh no", + "startline": 1, + "endline": 2, + } +} + +`, + }) + + scanner := generic.NewYamlScanner(rego.WithPolicyDirs("rules")) + + results, err := scanner.ScanFS(context.TODO(), fsys, "code") + require.NoError(t, err) + + require.Len(t, results.GetFailed(), 1) + + assert.Equal(t, scan.Rule{ + AVDID: "AVD-AB-0123", + Aliases: []string{"ABC123"}, + ShortCode: "short", + Summary: "title", + Explanation: "description", + Impact: "", + Resolution: "actions", + Provider: "yaml", + Service: "general", + Links: []string{"https://example.com"}, + Severity: "CRITICAL", + Terraform: &scan.EngineMetadata{}, + CloudFormation: &scan.EngineMetadata{}, + CustomChecks: scan.CustomChecks{ + Terraform: (*scan.TerraformCustomCheck)(nil)}, + RegoPackage: "data.builtin.yaml.lol", + Frameworks: map[framework.Framework][]string{ + framework.Default: {}, + }, + }, + results.GetFailed()[0].Rule(), + ) +} + +func TestTomlParser(t *testing.T) { + fsys := testutil.CreateFS(t, map[string]string{ + "/code/code.toml": ` +[x] +y = 123 +z = ["a", "b", "c"] +`, + "/rules/rule.rego": `package builtin.toml.lol + +__rego_metadata__ := { + "id": "ABC123", + "avd_id": "AVD-AB-0123", + "title": "title", + "short_code": "short", + "severity": "CRITICAL", + "type": "TOML Check", + "description": "description", + "recommended_actions": "actions", + "url": "https://example.com", +} + +__rego_input__ := { + "combine": false, + "selector": [{"type": "toml"}], +} + +deny[res] { + input.x.y == 123 + res := { + "msg": "oh no", + "startline": 1, + "endline": 2, + } +} + +`, + }) + + scanner := generic.NewTomlScanner(rego.WithPolicyDirs("rules")) + + results, err := scanner.ScanFS(context.TODO(), fsys, "code") + require.NoError(t, err) + + require.Len(t, results.GetFailed(), 1) + + assert.Equal(t, scan.Rule{ + AVDID: "AVD-AB-0123", + Aliases: []string{"ABC123"}, + ShortCode: "short", + Summary: "title", + Explanation: "description", + Impact: "", + Resolution: "actions", + Provider: "toml", + Service: "general", + Links: []string{"https://example.com"}, + Severity: "CRITICAL", + Terraform: &scan.EngineMetadata{}, + CloudFormation: &scan.EngineMetadata{}, + CustomChecks: scan.CustomChecks{ + Terraform: (*scan.TerraformCustomCheck)(nil)}, + RegoPackage: "data.builtin.toml.lol", + Frameworks: map[framework.Framework][]string{ + framework.Default: {}, + }, + }, + results.GetFailed()[0].Rule(), + ) +} diff --git a/pkg/iac/scanners/json/parser/parser.go b/pkg/iac/scanners/json/parser/parser.go deleted file mode 100644 index c97e54b40bd9..000000000000 --- a/pkg/iac/scanners/json/parser/parser.go +++ /dev/null @@ -1,65 +0,0 @@ -package parser - -import ( - "context" - "encoding/json" - "io/fs" - "path/filepath" - - "github.com/aquasecurity/trivy/pkg/log" -) - -type Parser struct { - logger *log.Logger -} - -// New creates a new parser -func New() *Parser { - return &Parser{ - logger: log.WithPrefix("json parser"), - } -} - -func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) (map[string]any, error) { - - files := make(map[string]any) - if err := fs.WalkDir(target, filepath.ToSlash(path), func(path string, entry fs.DirEntry, err error) error { - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - if err != nil { - return err - } - if entry.IsDir() { - return nil - } - - df, err := p.ParseFile(ctx, target, path) - if err != nil { - p.logger.Error("Parse error", log.FilePath(path), log.Err(err)) - return nil - } - - files[path] = df - return nil - }); err != nil { - return nil, err - } - return files, nil -} - -// ParseFile parses Dockerfile content from the provided filesystem path. -func (p *Parser) ParseFile(_ context.Context, fsys fs.FS, path string) (any, error) { - f, err := fsys.Open(filepath.ToSlash(path)) - if err != nil { - return nil, err - } - defer func() { _ = f.Close() }() - var target any - if err := json.NewDecoder(f).Decode(&target); err != nil { - return nil, err - } - return target, nil -} diff --git a/pkg/iac/scanners/json/parser/parser_test.go b/pkg/iac/scanners/json/parser/parser_test.go deleted file mode 100644 index a47868fad0ed..000000000000 --- a/pkg/iac/scanners/json/parser/parser_test.go +++ /dev/null @@ -1,51 +0,0 @@ -package parser - -import ( - "context" - "testing" - - "github.com/liamg/memoryfs" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_Parser(t *testing.T) { - input := `{ "x": { "y": 123, "z": ["a", "b", "c"]}}` - - memfs := memoryfs.New() - err := memfs.WriteFile("something.json", []byte(input), 0644) - require.NoError(t, err) - - data, err := New().ParseFile(context.TODO(), memfs, "something.json") - require.NoError(t, err) - - msi, ok := data.(map[string]any) - require.True(t, ok) - - xObj, ok := msi["x"] - require.True(t, ok) - - xMsi, ok := xObj.(map[string]any) - require.True(t, ok) - - yRaw, ok := xMsi["y"] - require.True(t, ok) - - y, ok := yRaw.(float64) - require.True(t, ok) - - assert.InEpsilon(t, 123.0, y, 0.0001) - - zRaw, ok := xMsi["z"] - require.True(t, ok) - - z, ok := zRaw.([]any) - require.True(t, ok) - - require.Len(t, z, 3) - - assert.Equal(t, "a", z[0]) - assert.Equal(t, "b", z[1]) - assert.Equal(t, "c", z[2]) - -} diff --git a/pkg/iac/scanners/json/scanner.go b/pkg/iac/scanners/json/scanner.go deleted file mode 100644 index f46f9cc56bc9..000000000000 --- a/pkg/iac/scanners/json/scanner.go +++ /dev/null @@ -1,113 +0,0 @@ -package json - -import ( - "context" - "io/fs" - "sync" - - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners" - "github.com/aquasecurity/trivy/pkg/iac/scanners/json/parser" - "github.com/aquasecurity/trivy/pkg/iac/scanners/options" - "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/log" -) - -var _ scanners.FSScanner = (*Scanner)(nil) -var _ options.ConfigurableScanner = (*Scanner)(nil) - -type Scanner struct { - mu sync.Mutex - logger *log.Logger - parser *parser.Parser - regoScanner *rego.Scanner - options []options.ScannerOption -} - -func (s *Scanner) SetIncludeDeprecatedChecks(bool) {} -func (s *Scanner) SetRegoOnly(bool) {} -func (s *Scanner) SetFrameworks(frameworks []framework.Framework) {} - -func NewScanner(opts ...options.ScannerOption) *Scanner { - s := &Scanner{ - options: opts, - logger: log.WithPrefix("json scanner"), - parser: parser.New(), - } - for _, opt := range opts { - opt(s) - } - return s -} - -func (s *Scanner) Name() string { - return "JSON" -} - -func (s *Scanner) ScanFS(ctx context.Context, fsys fs.FS, path string) (scan.Results, error) { - - files, err := s.parser.ParseFS(ctx, fsys, path) - if err != nil { - return nil, err - } - - if len(files) == 0 { - return nil, nil - } - - var inputs []rego.Input - for path, file := range files { - inputs = append(inputs, rego.Input{ - Path: path, - FS: fsys, - Contents: file, - }) - } - - results, err := s.scanRego(ctx, fsys, inputs...) - if err != nil { - return nil, err - } - return results, nil -} - -func (s *Scanner) ScanFile(ctx context.Context, fsys fs.FS, path string) (scan.Results, error) { - parsed, err := s.parser.ParseFile(ctx, fsys, path) - if err != nil { - return nil, err - } - s.logger.Debug("Scanning", log.FilePath(path)) - return s.scanRego(ctx, fsys, rego.Input{ - Path: path, - Contents: parsed, - }) -} - -func (s *Scanner) initRegoScanner(srcFS fs.FS) (*rego.Scanner, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.regoScanner != nil { - return s.regoScanner, nil - } - regoScanner := rego.NewScanner(types.SourceJSON, s.options...) - if err := regoScanner.LoadPolicies(srcFS); err != nil { - return nil, err - } - s.regoScanner = regoScanner - return regoScanner, nil -} - -func (s *Scanner) scanRego(ctx context.Context, srcFS fs.FS, inputs ...rego.Input) (scan.Results, error) { - regoScanner, err := s.initRegoScanner(srcFS) - if err != nil { - return nil, err - } - results, err := regoScanner.ScanInput(ctx, inputs...) - if err != nil { - return nil, err - } - results.SetSourceAndFilesystem("", srcFS, false) - return results, nil -} diff --git a/pkg/iac/scanners/json/scanner_test.go b/pkg/iac/scanners/json/scanner_test.go deleted file mode 100644 index 6656f70b2e25..000000000000 --- a/pkg/iac/scanners/json/scanner_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package json - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" -) - -func Test_BasicScan(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "/code/data.json": `{ "x": { "y": 123, "z": ["a", "b", "c"]}}`, - "/rules/rule.rego": `package builtin.json.lol - -__rego_metadata__ := { - "id": "ABC123", - "avd_id": "AVD-AB-0123", - "title": "title", - "short_code": "short", - "severity": "CRITICAL", - "type": "JSON Check", - "description": "description", - "recommended_actions": "actions", - "url": "https://example.com", -} - -__rego_input__ := { - "combine": false, - "selector": [{"type": "json"}], -} - -deny[res] { - input.x.y == 123 - res := { - "msg": "oh no", - "startline": 1, - "endline": 2, - } -} - -`, - }) - - scanner := NewScanner(rego.WithPolicyDirs("rules")) - - results, err := scanner.ScanFS(context.TODO(), fs, "code") - require.NoError(t, err) - - require.Len(t, results.GetFailed(), 1) - - assert.Equal(t, scan.Rule{ - AVDID: "AVD-AB-0123", - Aliases: []string{"ABC123"}, - ShortCode: "short", - Summary: "title", - Explanation: "description", - Impact: "", - Resolution: "actions", - Provider: "json", - Service: "general", - Links: []string{"https://example.com"}, - Severity: "CRITICAL", - Terraform: &scan.EngineMetadata{}, - CloudFormation: &scan.EngineMetadata{}, - CustomChecks: scan.CustomChecks{ - Terraform: (*scan.TerraformCustomCheck)(nil), - }, - RegoPackage: "data.builtin.json.lol", - Frameworks: map[framework.Framework][]string{ - framework.Default: {}, - }, - }, results.GetFailed()[0].Rule()) -} diff --git a/pkg/iac/scanners/toml/parser/parser.go b/pkg/iac/scanners/toml/parser/parser.go deleted file mode 100644 index 95444389d74d..000000000000 --- a/pkg/iac/scanners/toml/parser/parser.go +++ /dev/null @@ -1,65 +0,0 @@ -package parser - -import ( - "context" - "io/fs" - "path/filepath" - - "github.com/BurntSushi/toml" - - "github.com/aquasecurity/trivy/pkg/log" -) - -type Parser struct { - logger *log.Logger -} - -// New creates a new parser -func New() *Parser { - return &Parser{ - logger: log.WithPrefix("toml parser"), - } -} - -func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) (map[string]any, error) { - - files := make(map[string]any) - if err := fs.WalkDir(target, filepath.ToSlash(path), func(path string, entry fs.DirEntry, err error) error { - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - if err != nil { - return err - } - if entry.IsDir() { - return nil - } - - df, err := p.ParseFile(ctx, target, path) - if err != nil { - p.logger.Error("Parse error", log.FilePath(path), log.Err(err)) - return nil - } - files[path] = df - return nil - }); err != nil { - return nil, err - } - return files, nil -} - -// ParseFile parses toml content from the provided filesystem path. -func (p *Parser) ParseFile(_ context.Context, fsys fs.FS, path string) (any, error) { - f, err := fsys.Open(filepath.ToSlash(path)) - if err != nil { - return nil, err - } - defer func() { _ = f.Close() }() - var target any - if _, err := toml.NewDecoder(f).Decode(&target); err != nil { - return nil, err - } - return target, nil -} diff --git a/pkg/iac/scanners/toml/parser/parser_test.go b/pkg/iac/scanners/toml/parser/parser_test.go deleted file mode 100644 index 1c9e9bdc341e..000000000000 --- a/pkg/iac/scanners/toml/parser/parser_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package parser - -import ( - "context" - "testing" - - "github.com/liamg/memoryfs" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_Parser(t *testing.T) { - input := ` -[x] -y = 123 -z = ["a", "b", "c"] -` - - memfs := memoryfs.New() - err := memfs.WriteFile("something.yaml", []byte(input), 0644) - require.NoError(t, err) - - data, err := New().ParseFile(context.TODO(), memfs, "something.yaml") - require.NoError(t, err) - - msi, ok := data.(map[string]any) - require.True(t, ok) - - xObj, ok := msi["x"] - require.True(t, ok) - - xMsi, ok := xObj.(map[string]any) - require.True(t, ok) - - yRaw, ok := xMsi["y"] - require.True(t, ok) - - y, ok := yRaw.(int64) - require.True(t, ok) - - assert.Equal(t, int64(123), y) - - zRaw, ok := xMsi["z"] - require.True(t, ok) - - z, ok := zRaw.([]any) - require.True(t, ok) - - require.Len(t, z, 3) - - assert.Equal(t, "a", z[0]) - assert.Equal(t, "b", z[1]) - assert.Equal(t, "c", z[2]) - -} diff --git a/pkg/iac/scanners/toml/scanner.go b/pkg/iac/scanners/toml/scanner.go deleted file mode 100644 index 216d0b1f9697..000000000000 --- a/pkg/iac/scanners/toml/scanner.go +++ /dev/null @@ -1,111 +0,0 @@ -package toml - -import ( - "context" - "io/fs" - "sync" - - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners/options" - "github.com/aquasecurity/trivy/pkg/iac/scanners/toml/parser" - "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/log" -) - -var _ options.ConfigurableScanner = (*Scanner)(nil) - -type Scanner struct { - mu sync.Mutex - logger *log.Logger - options []options.ScannerOption - parser *parser.Parser - regoScanner *rego.Scanner -} - -func (s *Scanner) SetIncludeDeprecatedChecks(bool) {} -func (s *Scanner) SetRegoOnly(bool) {} -func (s *Scanner) SetFrameworks(frameworks []framework.Framework) {} - -func (s *Scanner) Name() string { - return "TOML" -} - -func NewScanner(opts ...options.ScannerOption) *Scanner { - s := &Scanner{ - options: opts, - logger: log.WithPrefix("toml scanner"), - } - for _, opt := range opts { - opt(s) - } - s.parser = parser.New() - return s -} - -func (s *Scanner) ScanFS(ctx context.Context, fsys fs.FS, path string) (scan.Results, error) { - - files, err := s.parser.ParseFS(ctx, fsys, path) - if err != nil { - return nil, err - } - - if len(files) == 0 { - return nil, nil - } - - var inputs []rego.Input - for path, file := range files { - inputs = append(inputs, rego.Input{ - Path: path, - Contents: file, - FS: fsys, - }) - } - - results, err := s.scanRego(ctx, fsys, inputs...) - if err != nil { - return nil, err - } - return results, nil -} - -func (s *Scanner) ScanFile(ctx context.Context, fsys fs.FS, path string) (scan.Results, error) { - parsed, err := s.parser.ParseFile(ctx, fsys, path) - if err != nil { - return nil, err - } - s.logger.Debug("Scanning", log.FilePath(path)) - return s.scanRego(ctx, fsys, rego.Input{ - Path: path, - Contents: parsed, - }) -} - -func (s *Scanner) initRegoScanner(srcFS fs.FS) (*rego.Scanner, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.regoScanner != nil { - return s.regoScanner, nil - } - regoScanner := rego.NewScanner(types.SourceTOML, s.options...) - if err := regoScanner.LoadPolicies(srcFS); err != nil { - return nil, err - } - s.regoScanner = regoScanner - return regoScanner, nil -} - -func (s *Scanner) scanRego(ctx context.Context, srcFS fs.FS, inputs ...rego.Input) (scan.Results, error) { - regoScanner, err := s.initRegoScanner(srcFS) - if err != nil { - return nil, err - } - results, err := regoScanner.ScanInput(ctx, inputs...) - if err != nil { - return nil, err - } - results.SetSourceAndFilesystem("", srcFS, false) - return results, nil -} diff --git a/pkg/iac/scanners/toml/scanner_test.go b/pkg/iac/scanners/toml/scanner_test.go deleted file mode 100644 index ea5819448288..000000000000 --- a/pkg/iac/scanners/toml/scanner_test.go +++ /dev/null @@ -1,85 +0,0 @@ -package toml - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" -) - -func Test_BasicScan(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "/code/code.toml": ` -[x] -y = 123 -z = ["a", "b", "c"] -`, - "/rules/rule.rego": `package builtin.toml.lol - -__rego_metadata__ := { - "id": "ABC123", - "avd_id": "AVD-AB-0123", - "title": "title", - "short_code": "short", - "severity": "CRITICAL", - "type": "TOML Check", - "description": "description", - "recommended_actions": "actions", - "url": "https://example.com", -} - -__rego_input__ := { - "combine": false, - "selector": [{"type": "toml"}], -} - -deny[res] { - input.x.y == 123 - res := { - "msg": "oh no", - "startline": 1, - "endline": 2, - } -} - -`, - }) - - scanner := NewScanner(rego.WithPolicyDirs("rules")) - - results, err := scanner.ScanFS(context.TODO(), fs, "code") - require.NoError(t, err) - - require.Len(t, results.GetFailed(), 1) - - assert.Equal(t, scan.Rule{ - AVDID: "AVD-AB-0123", - Aliases: []string{"ABC123"}, - ShortCode: "short", - Summary: "title", - Explanation: "description", - Impact: "", - Resolution: "actions", - Provider: "toml", - Service: "general", - Links: []string{"https://example.com"}, - Severity: "CRITICAL", - Terraform: &scan.EngineMetadata{}, - CloudFormation: &scan.EngineMetadata{}, - CustomChecks: scan.CustomChecks{ - Terraform: (*scan.TerraformCustomCheck)(nil)}, - RegoPackage: "data.builtin.toml.lol", - Frameworks: map[framework.Framework][]string{ - framework.Default: {}, - }, - }, - results.GetFailed()[0].Rule(), - ) -} diff --git a/pkg/iac/scanners/yaml/parser/parser.go b/pkg/iac/scanners/yaml/parser/parser.go deleted file mode 100644 index 9b1f026ae248..000000000000 --- a/pkg/iac/scanners/yaml/parser/parser.go +++ /dev/null @@ -1,86 +0,0 @@ -package parser - -import ( - "bytes" - "context" - "io" - "io/fs" - "path/filepath" - - "gopkg.in/yaml.v3" - - "github.com/aquasecurity/trivy/pkg/log" -) - -type Parser struct { - logger *log.Logger -} - -// New creates a new YAML parser -func New() *Parser { - return &Parser{ - logger: log.WithPrefix("yaml parser"), - } -} - -func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) (map[string][]any, error) { - - files := make(map[string][]any) - if err := fs.WalkDir(target, filepath.ToSlash(path), func(path string, entry fs.DirEntry, err error) error { - - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - if err != nil { - return err - } - if entry.IsDir() { - return nil - } - - df, err := p.ParseFile(ctx, target, path) - if err != nil { - p.logger.Error("Parse error", log.FilePath(path), log.Err(err)) - return nil - } - files[path] = df - return nil - }); err != nil { - return nil, err - } - return files, nil -} - -// ParseFile parses yaml content from the provided filesystem path. -func (p *Parser) ParseFile(_ context.Context, fsys fs.FS, path string) ([]any, error) { - f, err := fsys.Open(filepath.ToSlash(path)) - if err != nil { - return nil, err - } - defer func() { _ = f.Close() }() - - contents, err := io.ReadAll(f) - if err != nil { - return nil, err - } - - var results []any - - marker := []byte("\n---\n") - altMarker := []byte("\r\n---\r\n") - if bytes.Contains(contents, altMarker) { - marker = altMarker - } - - for _, partial := range bytes.Split(contents, marker) { - var target any - if err := yaml.Unmarshal(partial, &target); err != nil { - return nil, err - } - results = append(results, target) - } - - return results, nil -} diff --git a/pkg/iac/scanners/yaml/parser/parser_test.go b/pkg/iac/scanners/yaml/parser/parser_test.go deleted file mode 100644 index f7817d5990fa..000000000000 --- a/pkg/iac/scanners/yaml/parser/parser_test.go +++ /dev/null @@ -1,150 +0,0 @@ -package parser - -import ( - "context" - "testing" - - "github.com/liamg/memoryfs" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func Test_Parser(t *testing.T) { - input := `--- -x: - y: 123 - z: - - a - - b - - c -` - - memfs := memoryfs.New() - err := memfs.WriteFile("something.yaml", []byte(input), 0644) - require.NoError(t, err) - - data, err := New().ParseFile(context.TODO(), memfs, "something.yaml") - require.NoError(t, err) - - assert.Len(t, data, 1) - - msi, ok := data[0].(map[string]any) - require.True(t, ok) - - xObj, ok := msi["x"] - require.True(t, ok) - - xMsi, ok := xObj.(map[string]any) - require.True(t, ok) - - yRaw, ok := xMsi["y"] - require.True(t, ok) - - y, ok := yRaw.(int) - require.True(t, ok) - - assert.Equal(t, 123, y) - - zRaw, ok := xMsi["z"] - require.True(t, ok) - - z, ok := zRaw.([]any) - require.True(t, ok) - - require.Len(t, z, 3) - - assert.Equal(t, "a", z[0]) - assert.Equal(t, "b", z[1]) - assert.Equal(t, "c", z[2]) - -} - -func Test_Parser_WithSeparatedContent(t *testing.T) { - input := `--- -x: - y: 123 - z: - - a - - b - - c ---- -x: - y: 456 - z: - - x - - y - - z -` - - memfs := memoryfs.New() - err := memfs.WriteFile("something.yaml", []byte(input), 0644) - require.NoError(t, err) - - data, err := New().ParseFile(context.TODO(), memfs, "something.yaml") - require.NoError(t, err) - - assert.Len(t, data, 2) - - { - msi, ok := data[0].(map[string]any) - require.True(t, ok) - - xObj, ok := msi["x"] - require.True(t, ok) - - xMsi, ok := xObj.(map[string]any) - require.True(t, ok) - - yRaw, ok := xMsi["y"] - require.True(t, ok) - - y, ok := yRaw.(int) - require.True(t, ok) - - assert.Equal(t, 123, y) - - zRaw, ok := xMsi["z"] - require.True(t, ok) - - z, ok := zRaw.([]any) - require.True(t, ok) - - require.Len(t, z, 3) - - assert.Equal(t, "a", z[0]) - assert.Equal(t, "b", z[1]) - assert.Equal(t, "c", z[2]) - } - - { - msi, ok := data[1].(map[string]any) - require.True(t, ok) - - xObj, ok := msi["x"] - require.True(t, ok) - - xMsi, ok := xObj.(map[string]any) - require.True(t, ok) - - yRaw, ok := xMsi["y"] - require.True(t, ok) - - y, ok := yRaw.(int) - require.True(t, ok) - - assert.Equal(t, 456, y) - - zRaw, ok := xMsi["z"] - require.True(t, ok) - - z, ok := zRaw.([]any) - require.True(t, ok) - - require.Len(t, z, 3) - - assert.Equal(t, "x", z[0]) - assert.Equal(t, "y", z[1]) - assert.Equal(t, "z", z[2]) - } - -} diff --git a/pkg/iac/scanners/yaml/scanner.go b/pkg/iac/scanners/yaml/scanner.go deleted file mode 100644 index 565d10df1f83..000000000000 --- a/pkg/iac/scanners/yaml/scanner.go +++ /dev/null @@ -1,113 +0,0 @@ -package yaml - -import ( - "context" - "io/fs" - "sync" - - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners/options" - "github.com/aquasecurity/trivy/pkg/iac/scanners/yaml/parser" - "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/log" -) - -var _ options.ConfigurableScanner = (*Scanner)(nil) - -type Scanner struct { - mu sync.Mutex - options []options.ScannerOption - logger *log.Logger - parser *parser.Parser - regoScanner *rego.Scanner -} - -func (s *Scanner) SetIncludeDeprecatedChecks(bool) {} -func (s *Scanner) SetRegoOnly(bool) {} -func (s *Scanner) SetFrameworks(frameworks []framework.Framework) {} - -func (s *Scanner) Name() string { - return "YAML" -} - -func NewScanner(opts ...options.ScannerOption) *Scanner { - s := &Scanner{ - options: opts, - logger: log.WithPrefix("yaml scanner"), - parser: parser.New(), - } - for _, opt := range opts { - opt(s) - } - return s -} - -func (s *Scanner) ScanFS(ctx context.Context, fsys fs.FS, path string) (scan.Results, error) { - - fileset, err := s.parser.ParseFS(ctx, fsys, path) - if err != nil { - return nil, err - } - - if len(fileset) == 0 { - return nil, nil - } - - var inputs []rego.Input - for path, files := range fileset { - for _, file := range files { - inputs = append(inputs, rego.Input{ - Path: path, - Contents: file, - FS: fsys, - }) - } - } - - results, err := s.scanRego(ctx, fsys, inputs...) - if err != nil { - return nil, err - } - return results, nil -} - -func (s *Scanner) ScanFile(ctx context.Context, fsys fs.FS, path string) (scan.Results, error) { - parsed, err := s.parser.ParseFile(ctx, fsys, path) - if err != nil { - return nil, err - } - s.logger.Debug("Scanning", log.String("path", path)) - return s.scanRego(ctx, fsys, rego.Input{ - Path: path, - Contents: parsed, - }) -} - -func (s *Scanner) initRegoScanner(srcFS fs.FS) (*rego.Scanner, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.regoScanner != nil { - return s.regoScanner, nil - } - regoScanner := rego.NewScanner(types.SourceYAML, s.options...) - if err := regoScanner.LoadPolicies(srcFS); err != nil { - return nil, err - } - s.regoScanner = regoScanner - return regoScanner, nil -} - -func (s *Scanner) scanRego(ctx context.Context, srcFS fs.FS, inputs ...rego.Input) (scan.Results, error) { - regoScanner, err := s.initRegoScanner(srcFS) - if err != nil { - return nil, err - } - results, err := regoScanner.ScanInput(ctx, inputs...) - if err != nil { - return nil, err - } - results.SetSourceAndFilesystem("", srcFS, false) - return results, nil -} diff --git a/pkg/iac/scanners/yaml/scanner_test.go b/pkg/iac/scanners/yaml/scanner_test.go deleted file mode 100644 index b7c4ec623887..000000000000 --- a/pkg/iac/scanners/yaml/scanner_test.go +++ /dev/null @@ -1,88 +0,0 @@ -package yaml - -import ( - "context" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" -) - -func Test_BasicScan(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "/code/data.yaml": `--- -x: - y: 123 - z: - - a - - b - - c -`, - "/rules/rule.rego": `package builtin.yaml.lol - -__rego_metadata__ := { - "id": "ABC123", - "avd_id": "AVD-AB-0123", - "title": "title", - "short_code": "short", - "severity": "CRITICAL", - "type": "YAML Check", - "description": "description", - "recommended_actions": "actions", - "url": "https://example.com", -} - -__rego_input__ := { - "combine": false, - "selector": [{"type": "yaml"}], -} - -deny[res] { - input.x.y == 123 - res := { - "msg": "oh no", - "startline": 1, - "endline": 2, - } -} - -`, - }) - - scanner := NewScanner(rego.WithPolicyDirs("rules")) - - results, err := scanner.ScanFS(context.TODO(), fs, "code") - require.NoError(t, err) - - require.Len(t, results.GetFailed(), 1) - - assert.Equal(t, scan.Rule{ - AVDID: "AVD-AB-0123", - Aliases: []string{"ABC123"}, - ShortCode: "short", - Summary: "title", - Explanation: "description", - Impact: "", - Resolution: "actions", - Provider: "yaml", - Service: "general", - Links: []string{"https://example.com"}, - Severity: "CRITICAL", - Terraform: &scan.EngineMetadata{}, - CloudFormation: &scan.EngineMetadata{}, - CustomChecks: scan.CustomChecks{ - Terraform: (*scan.TerraformCustomCheck)(nil)}, - RegoPackage: "data.builtin.yaml.lol", - Frameworks: map[framework.Framework][]string{ - framework.Default: {}, - }, - }, - results.GetFailed()[0].Rule(), - ) -} diff --git a/pkg/misconf/scanner.go b/pkg/misconf/scanner.go index 7bec3ed6fe14..04fc28bc5296 100644 --- a/pkg/misconf/scanner.go +++ b/pkg/misconf/scanner.go @@ -24,14 +24,13 @@ import ( cfscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation" cfparser "github.com/aquasecurity/trivy/pkg/iac/scanners/cloudformation/parser" dfscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/dockerfile" + "github.com/aquasecurity/trivy/pkg/iac/scanners/generic" "github.com/aquasecurity/trivy/pkg/iac/scanners/helm" - jsonscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/json" k8sscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/kubernetes" "github.com/aquasecurity/trivy/pkg/iac/scanners/options" "github.com/aquasecurity/trivy/pkg/iac/scanners/terraform" tfprawscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/terraformplan/snapshot" tfpjsonscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/terraformplan/tfjson" - yamlscanner "github.com/aquasecurity/trivy/pkg/iac/scanners/yaml" "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/mapfs" @@ -118,9 +117,9 @@ func NewScanner(t detection.FileType, opt ScannerOption) (*Scanner, error) { case detection.FileTypeTerraformPlanSnapshot: scanner = tfprawscanner.New(opts...) case detection.FileTypeYAML: - scanner = yamlscanner.NewScanner(opts...) + scanner = generic.NewYamlScanner(opts...) case detection.FileTypeJSON: - scanner = jsonscanner.NewScanner(opts...) + scanner = generic.NewJsonScanner(opts...) default: return nil, xerrors.Errorf("unknown file type: %s", t) } From 31aa20ab90d52dd6cbc364fe0ff257b59cf02187 Mon Sep 17 00:00:00 2001 From: Simon Deziel Date: Mon, 14 Oct 2024 00:45:56 -0400 Subject: [PATCH 031/108] docs: apt-transport-https is a transitional package (#7678) Signed-off-by: Simon Deziel --- docs/getting-started/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index 61f6e42871bb..fd8b19366e86 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -34,7 +34,7 @@ In this section you will find an aggregation of the different ways to install Tr Add repository setting to `/etc/apt/sources.list.d`. ``` bash - sudo apt-get install wget apt-transport-https gnupg + sudo apt-get install wget gnupg wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb generic main" | sudo tee -a /etc/apt/sources.list.d/trivy.list sudo apt-get update From 633a7abeea4287899392a24f2705f96dfeb7e312 Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Tue, 15 Oct 2024 15:24:10 +0400 Subject: [PATCH 032/108] feat(cli): rename `trivy auth` to `trivy registry` (#7727) Signed-off-by: knqyf263 --- contrib/Trivy.gitlab-ci.yml | 2 +- docs/docs/advanced/private-registries/index.md | 6 +++--- docs/docs/references/configuration/cli/trivy.md | 2 +- .../cli/{trivy_auth.md => trivy_registry.md} | 10 +++++----- .../{trivy_auth_login.md => trivy_registry_login.md} | 8 ++++---- ...trivy_auth_logout.md => trivy_registry_logout.md} | 8 ++++---- docs/docs/target/container_image.md | 2 +- integration/registry_test.go | 4 ++-- mkdocs.yml | 8 ++++---- pkg/commands/app.go | 12 ++++++------ 10 files changed, 31 insertions(+), 31 deletions(-) rename docs/docs/references/configuration/cli/{trivy_auth.md => trivy_registry.md} (73%) rename docs/docs/references/configuration/cli/{trivy_auth_login.md => trivy_registry_login.md} (81%) rename docs/docs/references/configuration/cli/{trivy_auth_logout.md => trivy_registry_logout.md} (80%) diff --git a/contrib/Trivy.gitlab-ci.yml b/contrib/Trivy.gitlab-ci.yml index a62e26301a20..f91588240346 100644 --- a/contrib/Trivy.gitlab-ci.yml +++ b/contrib/Trivy.gitlab-ci.yml @@ -14,7 +14,7 @@ Trivy_container_scanning: - apk add --no-cache curl docker-cli - curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin ${TRIVY_VERSION} - curl -sSL -o /tmp/trivy-gitlab.tpl https://github.com/aquasecurity/trivy/raw/${TRIVY_VERSION}/contrib/gitlab.tpl - - trivy auth login --username "$CI_REGISTRY_USER" --password "$CI_REGISTRY_PASSWORD" $CI_REGISTRY + - trivy registry login --username "$CI_REGISTRY_USER" --password "$CI_REGISTRY_PASSWORD" $CI_REGISTRY script: - trivy --exit-code 0 --cache-dir .trivycache/ --no-progress --format template --template "@/tmp/trivy-gitlab.tpl" -o gl-container-scanning-report.json $IMAGE cache: diff --git a/docs/docs/advanced/private-registries/index.md b/docs/docs/advanced/private-registries/index.md index 7bac7a2742d5..e40f2f8f7230 100644 --- a/docs/docs/advanced/private-registries/index.md +++ b/docs/docs/advanced/private-registries/index.md @@ -2,11 +2,11 @@ Trivy can download images from a private registry without the need for installin This makes it easy to run within a CI process. ## Login -You can log in to a private registry using the `trivy auth login` command. +You can log in to a private registry using the `trivy registry login` command. It uses the Docker configuration file (`~/.docker/config.json`) to store the credentials under the hood, and the configuration file path can be configured by `DOCKER_CONFIG` environment variable. ```shell -$ cat ~/my_password.txt | trivy auth login --username foo --password-stdin ghcr.io +$ cat ~/my_password.txt | trivy registry login --username foo --password-stdin ghcr.io $ trivy image ghcr.io/your/private_image ``` @@ -23,7 +23,7 @@ $ TRIVY_USERNAME=YOUR_USERNAME TRIVY_PASSWORD=YOUR_PASSWORD trivy image YOUR_PRI To mitigate this risk: 1. Set credentials cautiously and only when necessary. - 2. Prefer using `trivy auth config` to pre-configure credentials with specific registries, which ensures credentials are only sent to appropriate registries. + 2. Prefer using `trivy registry login` to pre-configure credentials with specific registries, which ensures credentials are only sent to appropriate registries. Trivy also supports providing credentials through CLI flags: diff --git a/docs/docs/references/configuration/cli/trivy.md b/docs/docs/references/configuration/cli/trivy.md index 2919e43cbe27..0f4faaecde0c 100644 --- a/docs/docs/references/configuration/cli/trivy.md +++ b/docs/docs/references/configuration/cli/trivy.md @@ -43,7 +43,6 @@ trivy [global flags] command [flags] target ### SEE ALSO -* [trivy auth](trivy_auth.md) - Authentication * [trivy clean](trivy_clean.md) - Remove cached files * [trivy config](trivy_config.md) - Scan config files for misconfigurations * [trivy convert](trivy_convert.md) - Convert Trivy JSON report into a different format @@ -52,6 +51,7 @@ trivy [global flags] command [flags] target * [trivy kubernetes](trivy_kubernetes.md) - [EXPERIMENTAL] Scan kubernetes cluster * [trivy module](trivy_module.md) - Manage modules * [trivy plugin](trivy_plugin.md) - Manage plugins +* [trivy registry](trivy_registry.md) - Manage registry authentication * [trivy repository](trivy_repository.md) - Scan a repository * [trivy rootfs](trivy_rootfs.md) - Scan rootfs * [trivy sbom](trivy_sbom.md) - Scan SBOM for vulnerabilities and licenses diff --git a/docs/docs/references/configuration/cli/trivy_auth.md b/docs/docs/references/configuration/cli/trivy_registry.md similarity index 73% rename from docs/docs/references/configuration/cli/trivy_auth.md rename to docs/docs/references/configuration/cli/trivy_registry.md index ba2eaa6a6715..4a42cea3f4df 100644 --- a/docs/docs/references/configuration/cli/trivy_auth.md +++ b/docs/docs/references/configuration/cli/trivy_registry.md @@ -1,11 +1,11 @@ -## trivy auth +## trivy registry -Authentication +Manage registry authentication ### Options ``` - -h, --help help for auth + -h, --help help for registry ``` ### Options inherited from parent commands @@ -24,6 +24,6 @@ Authentication ### SEE ALSO * [trivy](trivy.md) - Unified security scanner -* [trivy auth login](trivy_auth_login.md) - Log in to a registry -* [trivy auth logout](trivy_auth_logout.md) - Log out of a registry +* [trivy registry login](trivy_registry_login.md) - Log in to a registry +* [trivy registry logout](trivy_registry_logout.md) - Log out of a registry diff --git a/docs/docs/references/configuration/cli/trivy_auth_login.md b/docs/docs/references/configuration/cli/trivy_registry_login.md similarity index 81% rename from docs/docs/references/configuration/cli/trivy_auth_login.md rename to docs/docs/references/configuration/cli/trivy_registry_login.md index 8f2cfdb18abc..6e963b3ca9f4 100644 --- a/docs/docs/references/configuration/cli/trivy_auth_login.md +++ b/docs/docs/references/configuration/cli/trivy_registry_login.md @@ -1,16 +1,16 @@ -## trivy auth login +## trivy registry login Log in to a registry ``` -trivy auth login SERVER [flags] +trivy registry login SERVER [flags] ``` ### Examples ``` # Log in to reg.example.com - cat ~/my_password.txt | trivy auth login --username foo --password-stdin reg.example.com + cat ~/my_password.txt | trivy registry login --username foo --password-stdin reg.example.com ``` ### Options @@ -37,5 +37,5 @@ trivy auth login SERVER [flags] ### SEE ALSO -* [trivy auth](trivy_auth.md) - Authentication +* [trivy registry](trivy_registry.md) - Manage registry authentication diff --git a/docs/docs/references/configuration/cli/trivy_auth_logout.md b/docs/docs/references/configuration/cli/trivy_registry_logout.md similarity index 80% rename from docs/docs/references/configuration/cli/trivy_auth_logout.md rename to docs/docs/references/configuration/cli/trivy_registry_logout.md index 3a25958001cd..b3da44c5fe92 100644 --- a/docs/docs/references/configuration/cli/trivy_auth_logout.md +++ b/docs/docs/references/configuration/cli/trivy_registry_logout.md @@ -1,16 +1,16 @@ -## trivy auth logout +## trivy registry logout Log out of a registry ``` -trivy auth logout SERVER [flags] +trivy registry logout SERVER [flags] ``` ### Examples ``` # Log out of reg.example.com - trivy auth logout reg.example.com + trivy registry logout reg.example.com ``` ### Options @@ -34,5 +34,5 @@ trivy auth logout SERVER [flags] ### SEE ALSO -* [trivy auth](trivy_auth.md) - Authentication +* [trivy registry](trivy_registry.md) - Manage registry authentication diff --git a/docs/docs/target/container_image.md b/docs/docs/target/container_image.md index 6f514db29fb5..7c703980de15 100644 --- a/docs/docs/target/container_image.md +++ b/docs/docs/target/container_image.md @@ -297,7 +297,7 @@ Trivy supports registries that comply with the following specifications. - [Docker Registry HTTP API V2](https://docs.docker.com/registry/spec/api/) - [OCI Distribution Specification](https://github.com/opencontainers/distribution-spec) -You can configure credentials with `trivy auth login`. +You can configure credentials with `trivy registry login`. See [here](../advanced/private-registries/index.md) for the detail. ### Tar Files diff --git a/integration/registry_test.go b/integration/registry_test.go index ce77db5ad74a..aded09adce35 100644 --- a/integration/registry_test.go +++ b/integration/registry_test.go @@ -184,7 +184,7 @@ func TestRegistry(t *testing.T) { golden: "testdata/alpine-310.json.golden", }, { - name: "authenticate with 'trivy auth login'", + name: "authenticate with 'trivy registry login'", imageName: "alpine:3.10", imageFile: "testdata/fixtures/images/alpine-310.tar.gz", os: "alpine 3.10.2", @@ -295,7 +295,7 @@ func setupEnv(t *testing.T, imageRef name.Reference, baseDir string, opt registr case opt.AuthLogin: t.Setenv("DOCKER_CONFIG", t.TempDir()) err := execute([]string{ - "auth", + "registry", "login", "--username", opt.Username, diff --git a/mkdocs.yml b/mkdocs.yml index 1d3d6276e2c3..00e458e15a91 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -158,10 +158,6 @@ nav: - Configuration: - CLI: - Overview: docs/references/configuration/cli/trivy.md - - Auth: - - Auth: docs/references/configuration/cli/trivy_auth.md - - Auth Login: docs/references/configuration/cli/trivy_auth_login.md - - Auth Logout: docs/references/configuration/cli/trivy_auth_logout.md - Clean: docs/references/configuration/cli/trivy_clean.md - Config: docs/references/configuration/cli/trivy_config.md - Convert: docs/references/configuration/cli/trivy_convert.md @@ -182,6 +178,10 @@ nav: - Plugin Update: docs/references/configuration/cli/trivy_plugin_update.md - Plugin Upgrade: docs/references/configuration/cli/trivy_plugin_upgrade.md - Plugin Search: docs/references/configuration/cli/trivy_plugin_search.md + - Registry: + - Registry: docs/references/configuration/cli/trivy_registry.md + - Registry Login: docs/references/configuration/cli/trivy_registry_login.md + - Registry Logout: docs/references/configuration/cli/trivy_registry_logout.md - Repository: docs/references/configuration/cli/trivy_repository.md - Rootfs: docs/references/configuration/cli/trivy_rootfs.md - SBOM: docs/references/configuration/cli/trivy_sbom.md diff --git a/pkg/commands/app.go b/pkg/commands/app.go index eec063a3eba3..dbe3d54ba373 100644 --- a/pkg/commands/app.go +++ b/pkg/commands/app.go @@ -100,7 +100,7 @@ func NewApp() *cobra.Command { NewVersionCommand(globalFlags), NewVMCommand(globalFlags), NewCleanCommand(globalFlags), - NewAuthCommand(globalFlags), + NewRegistryCommand(globalFlags), NewVEXCommand(globalFlags), ) @@ -1235,11 +1235,11 @@ func NewCleanCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { return cmd } -func NewAuthCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { +func NewRegistryCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { cmd := &cobra.Command{ - Use: "auth [flags]", + Use: "registry [flags]", GroupID: groupUtility, - Short: "Authentication", + Short: "Manage registry authentication", SilenceErrors: true, SilenceUsage: true, } @@ -1255,7 +1255,7 @@ func NewAuthCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { SilenceErrors: true, SilenceUsage: true, Example: ` # Log in to reg.example.com - cat ~/my_password.txt | trivy auth login --username foo --password-stdin reg.example.com`, + cat ~/my_password.txt | trivy registry login --username foo --password-stdin reg.example.com`, Args: cobra.ExactArgs(1), PreRunE: func(cmd *cobra.Command, args []string) error { if err := loginFlags.Bind(cmd); err != nil { @@ -1277,7 +1277,7 @@ func NewAuthCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command { SilenceErrors: true, SilenceUsage: true, Example: ` # Log out of reg.example.com - trivy auth logout reg.example.com`, + trivy registry logout reg.example.com`, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { return auth.Logout(cmd.Context(), args[0]) From ad914123c4d203af1e1da6b7e2d3e49d9d3831d8 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Wed, 16 Oct 2024 10:15:17 +0600 Subject: [PATCH 033/108] feat(misconf): public network support for Azure Storage Account (#7601) Signed-off-by: nikpivkin --- pkg/iac/adapters/arm/storage/adapt.go | 6 +++++ pkg/iac/adapters/arm/storage/adapt_test.go | 27 ++++++++++--------- .../adapters/terraform/azure/storage/adapt.go | 3 ++- .../terraform/azure/storage/adapt_test.go | 22 ++++++++++++--- pkg/iac/providers/azure/storage/storage.go | 15 ++++++----- pkg/iac/rego/schemas/cloud.json | 4 +++ 6 files changed, 54 insertions(+), 23 deletions(-) diff --git a/pkg/iac/adapters/arm/storage/adapt.go b/pkg/iac/adapters/arm/storage/adapt.go index 018949e24e10..186ab5beda9d 100644 --- a/pkg/iac/adapters/arm/storage/adapt.go +++ b/pkg/iac/adapters/arm/storage/adapt.go @@ -62,6 +62,12 @@ func adaptAccounts(deployment azure.Deployment) []storage.Account { MinimumTLSVersion: resource.Properties.GetMapValue("minimumTlsVersion").AsStringValue("", resource.Properties.GetMetadata()), Queues: queues, } + + publicNetworkAccess := resource.Properties.GetMapValue("publicNetworkAccess") + account.PublicNetworkAccess = types.Bool( + publicNetworkAccess.AsStringValue("Enabled", publicNetworkAccess.Metadata).EqualTo("Enabled"), + publicNetworkAccess.Metadata, + ) accounts = append(accounts, account) } return accounts diff --git a/pkg/iac/adapters/arm/storage/adapt_test.go b/pkg/iac/adapters/arm/storage/adapt_test.go index f4fd81f47ad2..a6965ca1560c 100644 --- a/pkg/iac/adapters/arm/storage/adapt_test.go +++ b/pkg/iac/adapters/arm/storage/adapt_test.go @@ -6,17 +6,17 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - azure2 "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" + "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" "github.com/aquasecurity/trivy/pkg/iac/types" ) func Test_AdaptStorageDefaults(t *testing.T) { - input := azure2.Deployment{ - Resources: []azure2.Resource{ + input := azure.Deployment{ + Resources: []azure.Resource{ { - Type: azure2.NewValue("Microsoft.Storage/storageAccounts", types.NewTestMetadata()), - Properties: azure2.NewValue(make(map[string]azure2.Value), types.NewTestMetadata()), + Type: azure.NewValue("Microsoft.Storage/storageAccounts", types.NewTestMetadata()), + Properties: azure.NewValue(make(map[string]azure.Value), types.NewTestMetadata()), }, }, } @@ -28,19 +28,21 @@ func Test_AdaptStorageDefaults(t *testing.T) { account := output.Accounts[0] assert.Equal(t, "", account.MinimumTLSVersion.Value()) assert.False(t, account.EnforceHTTPS.Value()) + assert.True(t, account.PublicNetworkAccess.Value()) } func Test_AdaptStorage(t *testing.T) { - input := azure2.Deployment{ - Resources: []azure2.Resource{ + input := azure.Deployment{ + Resources: []azure.Resource{ { - Type: azure2.NewValue("Microsoft.Storage/storageAccounts", types.NewTestMetadata()), - Name: azure2.Value{}, - Properties: azure2.NewValue(map[string]azure2.Value{ - "minimumTlsVersion": azure2.NewValue("TLS1_2", types.NewTestMetadata()), - "supportsHttpsTrafficOnly": azure2.NewValue(true, types.NewTestMetadata()), + Type: azure.NewValue("Microsoft.Storage/storageAccounts", types.NewTestMetadata()), + Name: azure.Value{}, + Properties: azure.NewValue(map[string]azure.Value{ + "minimumTlsVersion": azure.NewValue("TLS1_2", types.NewTestMetadata()), + "supportsHttpsTrafficOnly": azure.NewValue(true, types.NewTestMetadata()), + "publicNetworkAccess": azure.NewValue("Disabled", types.NewTestMetadata()), }, types.NewTestMetadata()), }, }, @@ -53,5 +55,6 @@ func Test_AdaptStorage(t *testing.T) { account := output.Accounts[0] assert.Equal(t, "TLS1_2", account.MinimumTLSVersion.Value()) assert.True(t, account.EnforceHTTPS.Value()) + assert.False(t, account.PublicNetworkAccess.Value()) } diff --git a/pkg/iac/adapters/terraform/azure/storage/adapt.go b/pkg/iac/adapters/terraform/azure/storage/adapt.go index 6a51cf1fca2b..3f125c3c828f 100644 --- a/pkg/iac/adapters/terraform/azure/storage/adapt.go +++ b/pkg/iac/adapters/terraform/azure/storage/adapt.go @@ -108,7 +108,8 @@ func adaptAccount(resource *terraform.Block) storage.Account { Metadata: resource.GetMetadata(), EnableLogging: iacTypes.BoolDefault(false, resource.GetMetadata()), }, - MinimumTLSVersion: iacTypes.StringDefault(minimumTlsVersionOneTwo, resource.GetMetadata()), + MinimumTLSVersion: iacTypes.StringDefault(minimumTlsVersionOneTwo, resource.GetMetadata()), + PublicNetworkAccess: resource.GetAttribute("public_network_access_enabled").AsBoolValueOrDefault(true, resource), } networkRulesBlocks := resource.GetBlocks("network_rules") diff --git a/pkg/iac/adapters/terraform/azure/storage/adapt_test.go b/pkg/iac/adapters/terraform/azure/storage/adapt_test.go index 87ffb1a919f4..5f576731d432 100644 --- a/pkg/iac/adapters/terraform/azure/storage/adapt_test.go +++ b/pkg/iac/adapters/terraform/azure/storage/adapt_test.go @@ -18,6 +18,20 @@ func Test_Adapt(t *testing.T) { terraform string expected storage.Storage }{ + { + name: "default", + terraform: `resource "azurerm_storage_account" "example" {}`, + expected: storage.Storage{ + Accounts: []storage.Account{ + { + PublicNetworkAccess: iacTypes.BoolTest(true), + MinimumTLSVersion: iacTypes.StringTest(minimumTlsVersionOneTwo), + EnforceHTTPS: iacTypes.BoolTest(true), + }, + {}, + }, + }, + }, { name: "defined", terraform: ` @@ -45,6 +59,7 @@ func Test_Adapt(t *testing.T) { } } min_tls_version = "TLS1_2" + public_network_access_enabled = false } resource "azurerm_storage_account_network_rules" "test" { @@ -65,9 +80,10 @@ func Test_Adapt(t *testing.T) { Accounts: []storage.Account{ { - Metadata: iacTypes.NewTestMetadata(), - EnforceHTTPS: iacTypes.Bool(true, iacTypes.NewTestMetadata()), - MinimumTLSVersion: iacTypes.String("TLS1_2", iacTypes.NewTestMetadata()), + Metadata: iacTypes.NewTestMetadata(), + EnforceHTTPS: iacTypes.Bool(true, iacTypes.NewTestMetadata()), + MinimumTLSVersion: iacTypes.String("TLS1_2", iacTypes.NewTestMetadata()), + PublicNetworkAccess: iacTypes.BoolTest(false), NetworkRules: []storage.NetworkRule{ { Metadata: iacTypes.NewTestMetadata(), diff --git a/pkg/iac/providers/azure/storage/storage.go b/pkg/iac/providers/azure/storage/storage.go index cccc5d55eda1..ce86ec8698cc 100755 --- a/pkg/iac/providers/azure/storage/storage.go +++ b/pkg/iac/providers/azure/storage/storage.go @@ -9,13 +9,14 @@ type Storage struct { } type Account struct { - Metadata iacTypes.Metadata - NetworkRules []NetworkRule - EnforceHTTPS iacTypes.BoolValue - Containers []Container - QueueProperties QueueProperties - MinimumTLSVersion iacTypes.StringValue - Queues []Queue + Metadata iacTypes.Metadata + NetworkRules []NetworkRule + EnforceHTTPS iacTypes.BoolValue + Containers []Container + QueueProperties QueueProperties + MinimumTLSVersion iacTypes.StringValue + Queues []Queue + PublicNetworkAccess iacTypes.BoolValue } type Queue struct { diff --git a/pkg/iac/rego/schemas/cloud.json b/pkg/iac/rego/schemas/cloud.json index b034f24fa104..d0ce7ffe515a 100644 --- a/pkg/iac/rego/schemas/cloud.json +++ b/pkg/iac/rego/schemas/cloud.json @@ -5396,6 +5396,10 @@ "$ref": "#/definitions/github.aaakk.us.kg.aquasecurity.trivy.pkg.iac.providers.azure.storage.NetworkRule" } }, + "publicnetworkaccess": { + "type": "object", + "$ref": "#/definitions/github.aaakk.us.kg.aquasecurity.trivy.pkg.iac.types.BoolValue" + }, "queueproperties": { "type": "object", "$ref": "#/definitions/github.aaakk.us.kg.aquasecurity.trivy.pkg.iac.providers.azure.storage.QueueProperties" From 83e5b83acc20861b28f72a0796fc46a7325898f6 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Wed, 16 Oct 2024 10:31:20 +0600 Subject: [PATCH 034/108] docs: add note about disabled DS016 check (#7724) Signed-off-by: nikpivkin --- docs/docs/target/container_image.md | 2 ++ .../analyzer/imgconf/dockerfile/dockerfile.go | 9 ++++--- pkg/misconf/scanner.go | 25 ++++++++++++++----- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/docs/docs/target/container_image.md b/docs/docs/target/container_image.md index 7c703980de15..f1c491071a3f 100644 --- a/docs/docs/target/container_image.md +++ b/docs/docs/target/container_image.md @@ -154,6 +154,8 @@ See https://avd.aquasec.com/misconfig/ds026 !!! tip You can see how each layer is created with `docker history`. +The [AVD-DS-0016](https://avd.aquasec.com/misconfig/dockerfile/general/avd-ds-0016/) check is disabled for this scan type, see [issue](https://github.com/aquasecurity/trivy/issues/7368) for details. + ### Secrets Trivy detects secrets on the configuration of container images. The image config is converted into JSON and Trivy scans the file for secrets. diff --git a/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go b/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go index c79a81f54c61..35a4fc12fea1 100644 --- a/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go +++ b/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go @@ -16,8 +16,11 @@ import ( "github.com/aquasecurity/trivy/pkg/misconf" ) -var disabledChecks = []string{ - "DS016", // See https://github.com/aquasecurity/trivy/issues/7368 +var disabledChecks = []misconf.DisabledCheck{ + { + ID: "DS016", Scanner: string(analyzer.TypeHistoryDockerfile), + Reason: "See https://github.com/aquasecurity/trivy/issues/7368", + }, } const analyzerVersion = 1 @@ -31,7 +34,7 @@ type historyAnalyzer struct { } func newHistoryAnalyzer(opts analyzer.ConfigAnalyzerOptions) (analyzer.ConfigAnalyzer, error) { - opts.MisconfScannerOption.DisabledCheckIDs = append(opts.MisconfScannerOption.DisabledCheckIDs, disabledChecks...) + opts.MisconfScannerOption.DisabledChecks = append(opts.MisconfScannerOption.DisabledChecks, disabledChecks...) s, err := misconf.NewScanner(detection.FileTypeDockerfile, opts.MisconfScannerOption) if err != nil { return nil, xerrors.Errorf("misconfiguration scanner error: %w", err) diff --git a/pkg/misconf/scanner.go b/pkg/misconf/scanner.go index 04fc28bc5296..7a3642dfe895 100644 --- a/pkg/misconf/scanner.go +++ b/pkg/misconf/scanner.go @@ -50,6 +50,12 @@ var enablediacTypes = map[detection.FileType]types.ConfigType{ detection.FileTypeYAML: types.YAML, } +type DisabledCheck struct { + ID string + Scanner string // For logging + Reason string // For logging +} + type ScannerOption struct { Trace bool RegoOnly bool @@ -74,9 +80,9 @@ type ScannerOption struct { FilePatterns []string ConfigFileSchemas []*ConfigFileSchema - DisabledCheckIDs []string - SkipFiles []string - SkipDirs []string + DisabledChecks []DisabledCheck + SkipFiles []string + SkipDirs []string } func (o *ScannerOption) Sort() { @@ -133,6 +139,7 @@ func NewScanner(t detection.FileType, opt ScannerOption) (*Scanner, error) { } func (s *Scanner) Scan(ctx context.Context, fsys fs.FS) ([]types.Misconfiguration, error) { + ctx = log.WithContextPrefix(ctx, log.PrefixMisconfiguration) newfs, err := s.filterFS(fsys) if err != nil { return nil, xerrors.Errorf("fs filter error: %w", err) @@ -141,12 +148,12 @@ func (s *Scanner) Scan(ctx context.Context, fsys fs.FS) ([]types.Misconfiguratio return nil, nil } - log.Debug("Scanning files for misconfigurations...", log.String("scanner", s.scanner.Name())) + log.DebugContext(ctx, "Scanning files for misconfigurations...", log.String("scanner", s.scanner.Name())) results, err := s.scanner.ScanFS(ctx, newfs, ".") if err != nil { var invalidContentError *cfparser.InvalidContentError if errors.As(err, &invalidContentError) { - log.Error("scan was broken with InvalidContentError", s.scanner.Name(), log.Err(err)) + log.ErrorContext(ctx, "scan was broken with InvalidContentError", s.scanner.Name(), log.Err(err)) return nil, nil } return nil, xerrors.Errorf("scan config error: %w", err) @@ -211,11 +218,17 @@ func (s *Scanner) filterFS(fsys fs.FS) (fs.FS, error) { } func scannerOptions(t detection.FileType, opt ScannerOption) ([]options.ScannerOption, error) { + disabledCheckIDs := lo.Map(opt.DisabledChecks, func(check DisabledCheck, _ int) string { + log.Info("Check disabled", log.Prefix(log.PrefixMisconfiguration), log.String("ID", check.ID), + log.String("scanner", check.Scanner), log.String("reason", check.Reason)) + return check.ID + }) + opts := []options.ScannerOption{ rego.WithEmbeddedPolicies(!opt.DisableEmbeddedPolicies), rego.WithEmbeddedLibraries(!opt.DisableEmbeddedLibraries), options.ScannerWithIncludeDeprecatedChecks(opt.IncludeDeprecatedChecks), - rego.WithDisabledCheckIDs(opt.DisabledCheckIDs...), + rego.WithDisabledCheckIDs(disabledCheckIDs...), } policyFS, policyPaths, err := CreatePolicyFS(opt.PolicyPaths) From bcfc37bb16c6ddb3faa439cac0cec036475f4d30 Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Thu, 17 Oct 2024 03:41:59 +0400 Subject: [PATCH 035/108] test: define constants for test images (#7739) Signed-off-by: knqyf263 Co-authored-by: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> --- .github/workflows/cache-test-images.yaml | 6 +- .github/workflows/test.yaml | 9 ++- integration/testimages.ini | 3 + internal/testutil/image.go | 67 +++++++++++++++++++ magefiles/fixture.go | 10 +-- pkg/attestation/attestation_test.go | 3 +- .../analyzer/language/java/jar/jar_test.go | 4 -- pkg/fanal/test/integration/containerd_test.go | 23 ++++--- pkg/fanal/test/integration/registry_test.go | 9 +-- 9 files changed, 104 insertions(+), 30 deletions(-) create mode 100644 integration/testimages.ini create mode 100644 internal/testutil/image.go diff --git a/.github/workflows/cache-test-images.yaml b/.github/workflows/cache-test-images.yaml index a03dc683e3ae..4b73cc2fb5bc 100644 --- a/.github/workflows/cache-test-images.yaml +++ b/.github/workflows/cache-test-images.yaml @@ -27,7 +27,8 @@ jobs: if: github.ref_name == 'main' id: image-digest run: | - IMAGE_LIST=$(skopeo list-tags docker://ghcr.io/aquasecurity/trivy-test-images) + source integration/testimages.ini + IMAGE_LIST=$(skopeo list-tags docker://$TEST_IMAGES) DIGEST=$(echo "$IMAGE_LIST" | sha256sum | cut -d' ' -f1) echo "digest=$DIGEST" >> $GITHUB_OUTPUT @@ -67,7 +68,8 @@ jobs: if: github.ref_name == 'main' id: image-digest run: | - IMAGE_LIST=$(skopeo list-tags docker://ghcr.io/aquasecurity/trivy-test-vm-images) + source integration/testimages.ini + IMAGE_LIST=$(skopeo list-tags docker://$TEST_VM_IMAGES) DIGEST=$(echo "$IMAGE_LIST" | sha256sum | cut -d' ' -f1) echo "digest=$DIGEST" >> $GITHUB_OUTPUT diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 74b0933c6ef6..8932a683c5bc 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -90,7 +90,8 @@ jobs: - name: Generate image list digest id: image-digest run: | - IMAGE_LIST=$(skopeo list-tags docker://ghcr.io/aquasecurity/trivy-test-images) + source integration/testimages.ini + IMAGE_LIST=$(skopeo list-tags docker://$TEST_IMAGES) DIGEST=$(echo "$IMAGE_LIST" | sha256sum | cut -d' ' -f1) echo "digest=$DIGEST" >> $GITHUB_OUTPUT @@ -147,7 +148,8 @@ jobs: - name: Generate image list digest id: image-digest run: | - IMAGE_LIST=$(skopeo list-tags docker://ghcr.io/aquasecurity/trivy-test-images) + source integration/testimages.ini + IMAGE_LIST=$(skopeo list-tags docker://$TEST_IMAGES) DIGEST=$(echo "$IMAGE_LIST" | sha256sum | cut -d' ' -f1) echo "digest=$DIGEST" >> $GITHUB_OUTPUT @@ -185,7 +187,8 @@ jobs: - name: Generate image list digest id: image-digest run: | - IMAGE_LIST=$(skopeo list-tags docker://ghcr.io/aquasecurity/trivy-test-vm-images) + source integration/testimages.ini + IMAGE_LIST=$(skopeo list-tags docker://$TEST_VM_IMAGES) DIGEST=$(echo "$IMAGE_LIST" | sha256sum | cut -d' ' -f1) echo "digest=$DIGEST" >> $GITHUB_OUTPUT diff --git a/integration/testimages.ini b/integration/testimages.ini new file mode 100644 index 000000000000..1a7b8ef1a0c0 --- /dev/null +++ b/integration/testimages.ini @@ -0,0 +1,3 @@ +# Configuration file for both shell scripts and Go programs +TEST_IMAGES=ghcr.io/aquasecurity/trivy-test-images +TEST_VM_IMAGES=ghcr.io/aquasecurity/trivy-test-vm-images diff --git a/internal/testutil/image.go b/internal/testutil/image.go new file mode 100644 index 000000000000..dba54bc23dff --- /dev/null +++ b/internal/testutil/image.go @@ -0,0 +1,67 @@ +package testutil + +import ( + "bufio" + "fmt" + "os" + "path/filepath" + "runtime" + "strings" +) + +var ( + testImages string + testVMImages string +) + +func init() { + _, b, _, _ := runtime.Caller(0) + currentDir := filepath.Dir(b) + f, err := os.Open(filepath.Join(currentDir, "..", "..", "integration", "testimages.ini")) + if err != nil { + panic(err) + } + defer f.Close() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + if strings.HasPrefix(scanner.Text(), "#") { + continue + } + parts := strings.SplitN(scanner.Text(), "=", 2) + if len(parts) == 2 { + key := strings.TrimSpace(parts[0]) + value := strings.TrimSpace(parts[1]) + switch key { + case "TEST_IMAGES": + testImages = value + case "TEST_VM_IMAGES": + testVMImages = value + } + } + } + if err = scanner.Err(); err != nil { + panic(err) + } +} + +func ImageName(subpath, tag, digest string) string { + return imageName(testImages, subpath, tag, digest) +} + +func VMImageName(subpath, tag, digest string) string { + return imageName(testVMImages, subpath, tag, digest) +} + +func imageName(img, subpath, tag, digest string) string { + if subpath != "" { + img = fmt.Sprintf("%s/%s", img, subpath) + } + if tag != "" { + img = fmt.Sprintf("%s:%s", img, tag) + } + if digest != "" { + img = fmt.Sprintf("%s@%s", img, digest) + } + return img +} diff --git a/magefiles/fixture.go b/magefiles/fixture.go index 0ed9ae8d4217..0112a8421133 100644 --- a/magefiles/fixture.go +++ b/magefiles/fixture.go @@ -10,13 +10,13 @@ import ( "github.com/google/go-containerregistry/pkg/crane" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/magefile/mage/sh" + + "github.com/aquasecurity/trivy/internal/testutil" ) func fixtureContainerImages() error { - const ( - testImages = "ghcr.io/aquasecurity/trivy-test-images" - dir = "integration/testdata/fixtures/images/" - ) + var testImages = testutil.ImageName("", "", "") + const dir = "integration/testdata/fixtures/images/" if err := os.MkdirAll(dir, 0750); err != nil { return err } @@ -48,8 +48,8 @@ func fixtureContainerImages() error { } func fixtureVMImages() error { + var testVMImages = testutil.VMImageName("", "", "") const ( - testVMImages = "ghcr.io/aquasecurity/trivy-test-vm-images" titleAnnotation = "org.opencontainers.image.title" dir = "integration/testdata/fixtures/vm-images/" ) diff --git a/pkg/attestation/attestation_test.go b/pkg/attestation/attestation_test.go index 763878f4ae41..de75b5676692 100644 --- a/pkg/attestation/attestation_test.go +++ b/pkg/attestation/attestation_test.go @@ -9,6 +9,7 @@ import ( slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" "github.com/stretchr/testify/require" + "github.com/aquasecurity/trivy/internal/testutil" "github.com/aquasecurity/trivy/pkg/attestation" ) @@ -27,7 +28,7 @@ func TestStatement_UnmarshalJSON(t *testing.T) { PredicateType: "cosign.sigstore.dev/attestation/v1", Subject: []in_toto.Subject{ { - Name: "ghcr.io/aquasecurity/trivy-test-images", + Name: testutil.ImageName("", "", ""), Digest: slsa.DigestSet{ "sha256": "72c42ed48c3a2db31b7dafe17d275b634664a708d901ec9fd57b1529280f01fb", }, diff --git a/pkg/fanal/analyzer/language/java/jar/jar_test.go b/pkg/fanal/analyzer/language/java/jar/jar_test.go index 146b60c1bbd6..58e7221066ac 100644 --- a/pkg/fanal/analyzer/language/java/jar/jar_test.go +++ b/pkg/fanal/analyzer/language/java/jar/jar_test.go @@ -18,10 +18,6 @@ import ( _ "modernc.org/sqlite" ) -const ( - defaultJavaDBRepository = "ghcr.io/aquasecurity/trivy-java-db" -) - func Test_javaLibraryAnalyzer_Analyze(t *testing.T) { tests := []struct { name string diff --git a/pkg/fanal/test/integration/containerd_test.go b/pkg/fanal/test/integration/containerd_test.go index d16ad3dac059..568af60528ea 100644 --- a/pkg/fanal/test/integration/containerd_test.go +++ b/pkg/fanal/test/integration/containerd_test.go @@ -27,6 +27,7 @@ import ( "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" + "github.com/aquasecurity/trivy/internal/testutil" "github.com/aquasecurity/trivy/pkg/cache" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" "github.com/aquasecurity/trivy/pkg/fanal/applier" @@ -77,7 +78,7 @@ func startContainerd(t *testing.T, ctx context.Context, hostPath string) { t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true") req := testcontainers.ContainerRequest{ Name: "containerd", - Image: "ghcr.io/aquasecurity/trivy-test-images/containerd:latest", + Image: testutil.ImageName("containerd", "latest", ""), Entrypoint: []string{ "/bin/sh", "-c", @@ -122,7 +123,7 @@ func TestContainerd_SearchLocalStoreByNameOrDigest(t *testing.T) { digest := "sha256:f12582b2f2190f350e3904462c1c23aaf366b4f76705e97b199f9bbded1d816a" basename := "hello" tag := "world" - importedImageOriginalName := "ghcr.io/aquasecurity/trivy-test-images:alpine-310" + importedImageOriginalName := testutil.ImageName("", "alpine-310", "") tests := []struct { name string @@ -299,15 +300,15 @@ func localImageTestWithNamespace(t *testing.T, namespace string) { }{ { name: "alpine 3.10", - imageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310", + imageName: testutil.ImageName("", "alpine-310", ""), tarArchive: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz", wantMetadata: artifact.ImageMetadata{ ID: "sha256:961769676411f082461f9ef46626dd7a2d1e2b2a38e6a44364bcbecf51e66dd4", DiffIDs: []string{ "sha256:03901b4a2ea88eeaad62dbe59b072b28b6efa00491962b8741081c5df50c65e0", }, - RepoTags: []string{"ghcr.io/aquasecurity/trivy-test-images:alpine-310"}, - RepoDigests: []string{"ghcr.io/aquasecurity/trivy-test-images@sha256:f12582b2f2190f350e3904462c1c23aaf366b4f76705e97b199f9bbded1d816a"}, + RepoTags: []string{testutil.ImageName("", "alpine-310", "")}, + RepoDigests: []string{testutil.ImageName("", "", "sha256:f12582b2f2190f350e3904462c1c23aaf366b4f76705e97b199f9bbded1d816a")}, ConfigFile: v1.ConfigFile{ Architecture: "amd64", Created: v1.Time{ @@ -347,7 +348,7 @@ func localImageTestWithNamespace(t *testing.T, namespace string) { }, { name: "vulnimage", - imageName: "ghcr.io/aquasecurity/trivy-test-images:vulnimage", + imageName: testutil.ImageName("", "vulnimage", ""), tarArchive: "../../../../integration/testdata/fixtures/images/vulnimage.tar.gz", wantMetadata: artifact.ImageMetadata{ ID: "sha256:c17083664da903e13e9092fa3a3a1aeee2431aa2728298e3dbcec72f26369c41", @@ -373,8 +374,8 @@ func localImageTestWithNamespace(t *testing.T, namespace string) { "sha256:ba17950e91742d6ac7055ea3a053fe764486658ca1ce8188f1e427b1fe2bc4da", "sha256:6ef42db7800507577383edf1937cb203b9b85f619feed6046594208748ceb52c", }, - RepoTags: []string{"ghcr.io/aquasecurity/trivy-test-images:vulnimage"}, - RepoDigests: []string{"ghcr.io/aquasecurity/trivy-test-images@sha256:e74abbfd81e00baaf464cf9e09f8b24926e5255171e3150a60aa341ce064688f"}, + RepoTags: []string{testutil.ImageName("", "vulnimage", "")}, + RepoDigests: []string{testutil.ImageName("", "", "sha256:e74abbfd81e00baaf464cf9e09f8b24926e5255171e3150a60aa341ce064688f")}, ConfigFile: v1.ConfigFile{ Architecture: "amd64", Created: v1.Time{ @@ -750,14 +751,14 @@ func TestContainerd_PullImage(t *testing.T) { }{ { name: "remote alpine 3.10", - imageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310", + imageName: testutil.ImageName("", "alpine-310", ""), wantMetadata: artifact.ImageMetadata{ ID: "sha256:961769676411f082461f9ef46626dd7a2d1e2b2a38e6a44364bcbecf51e66dd4", DiffIDs: []string{ "sha256:03901b4a2ea88eeaad62dbe59b072b28b6efa00491962b8741081c5df50c65e0", }, - RepoTags: []string{"ghcr.io/aquasecurity/trivy-test-images:alpine-310"}, - RepoDigests: []string{"ghcr.io/aquasecurity/trivy-test-images@sha256:72c42ed48c3a2db31b7dafe17d275b634664a708d901ec9fd57b1529280f01fb"}, + RepoTags: []string{testutil.ImageName("", "alpine-310", "")}, + RepoDigests: []string{testutil.ImageName("", "", "sha256:72c42ed48c3a2db31b7dafe17d275b634664a708d901ec9fd57b1529280f01fb")}, ConfigFile: v1.ConfigFile{ Architecture: "amd64", Created: v1.Time{ diff --git a/pkg/fanal/test/integration/registry_test.go b/pkg/fanal/test/integration/registry_test.go index 5b062e425729..4bca93f0ccba 100644 --- a/pkg/fanal/test/integration/registry_test.go +++ b/pkg/fanal/test/integration/registry_test.go @@ -20,6 +20,7 @@ import ( testcontainers "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" + "github.com/aquasecurity/trivy/internal/testutil" "github.com/aquasecurity/trivy/pkg/cache" "github.com/aquasecurity/trivy/pkg/fanal/analyzer" _ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/all" @@ -95,7 +96,7 @@ func TestTLSRegistry(t *testing.T) { }{ { name: "happy path", - imageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310", + imageName: testutil.ImageName("", "alpine-310", ""), imageFile: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz", option: types.ImageOptions{ RegistryOptions: types.RegistryOptions{ @@ -120,7 +121,7 @@ func TestTLSRegistry(t *testing.T) { }, { name: "happy path with docker login", - imageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310", + imageName: testutil.ImageName("", "alpine-310", ""), imageFile: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz", option: types.ImageOptions{ RegistryOptions: types.RegistryOptions{ @@ -140,7 +141,7 @@ func TestTLSRegistry(t *testing.T) { }, { name: "sad path: tls verify", - imageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310", + imageName: testutil.ImageName("", "alpine-310", ""), imageFile: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz", option: types.ImageOptions{ RegistryOptions: types.RegistryOptions{ @@ -156,7 +157,7 @@ func TestTLSRegistry(t *testing.T) { }, { name: "sad path: no credential", - imageName: "ghcr.io/aquasecurity/trivy-test-images:alpine-310", + imageName: testutil.ImageName("", "alpine-310", ""), imageFile: "../../../../integration/testdata/fixtures/images/alpine-310.tar.gz", option: types.ImageOptions{ RegistryOptions: types.RegistryOptions{ From 2eaa17e0717940b27a79050e2efd9213b71178c9 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Thu, 17 Oct 2024 08:29:39 +0600 Subject: [PATCH 036/108] feat(misconf): ssl_mode support for GCP SQL DB instance (#7564) Signed-off-by: nikpivkin --- pkg/iac/adapters/terraform/google/sql/adapt.go | 12 ++++-------- pkg/iac/adapters/terraform/google/sql/adapt_test.go | 2 ++ pkg/iac/providers/google/sql/sql.go | 1 + pkg/iac/rego/schemas/cloud.json | 4 ++++ 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/pkg/iac/adapters/terraform/google/sql/adapt.go b/pkg/iac/adapters/terraform/google/sql/adapt.go index 6418942384d4..6d68795bcfac 100644 --- a/pkg/iac/adapters/terraform/google/sql/adapt.go +++ b/pkg/iac/adapters/terraform/google/sql/adapt.go @@ -52,6 +52,7 @@ func adaptInstance(resource *terraform.Block) sql.DatabaseInstance { IPConfiguration: sql.IPConfiguration{ Metadata: resource.GetMetadata(), RequireTLS: iacTypes.BoolDefault(false, resource.GetMetadata()), + SSLMode: iacTypes.String("", resource.GetMetadata()), EnableIPv4: iacTypes.BoolDefault(true, resource.GetMetadata()), AuthorizedNetworks: nil, }, @@ -125,12 +126,6 @@ func adaptIPConfig(resource *terraform.Block) sql.IPConfiguration { CIDR iacTypes.StringValue } - tlsRequiredAttr := resource.GetAttribute("require_ssl") - tlsRequiredVal := tlsRequiredAttr.AsBoolValueOrDefault(false, resource) - - ipv4enabledAttr := resource.GetAttribute("ipv4_enabled") - ipv4enabledVal := ipv4enabledAttr.AsBoolValueOrDefault(true, resource) - authNetworksBlocks := resource.GetBlocks("authorized_networks") for _, authBlock := range authNetworksBlocks { nameVal := authBlock.GetAttribute("name").AsStringValueOrDefault("", authBlock) @@ -147,8 +142,9 @@ func adaptIPConfig(resource *terraform.Block) sql.IPConfiguration { return sql.IPConfiguration{ Metadata: resource.GetMetadata(), - RequireTLS: tlsRequiredVal, - EnableIPv4: ipv4enabledVal, + RequireTLS: resource.GetAttribute("require_ssl").AsBoolValueOrDefault(false, resource), + SSLMode: resource.GetAttribute("ssl_mode").AsStringValueOrDefault("", resource), + EnableIPv4: resource.GetAttribute("ipv4_enabled").AsBoolValueOrDefault(true, resource), AuthorizedNetworks: authorizedNetworks, } } diff --git a/pkg/iac/adapters/terraform/google/sql/adapt_test.go b/pkg/iac/adapters/terraform/google/sql/adapt_test.go index 29e89d6282b7..fd3207ed3547 100644 --- a/pkg/iac/adapters/terraform/google/sql/adapt_test.go +++ b/pkg/iac/adapters/terraform/google/sql/adapt_test.go @@ -34,6 +34,7 @@ func Test_Adapt(t *testing.T) { name = "internal" } require_ssl = true + ssl_mode = "TRUSTED_CLIENT_CERTIFICATE_REQUIRED" } } } @@ -67,6 +68,7 @@ func Test_Adapt(t *testing.T) { Metadata: iacTypes.NewTestMetadata(), RequireTLS: iacTypes.Bool(true, iacTypes.NewTestMetadata()), EnableIPv4: iacTypes.Bool(false, iacTypes.NewTestMetadata()), + SSLMode: iacTypes.StringTest("TRUSTED_CLIENT_CERTIFICATE_REQUIRED"), AuthorizedNetworks: []struct { Name iacTypes.StringValue CIDR iacTypes.StringValue diff --git a/pkg/iac/providers/google/sql/sql.go b/pkg/iac/providers/google/sql/sql.go index 18778dd1daef..672e78a42fbd 100755 --- a/pkg/iac/providers/google/sql/sql.go +++ b/pkg/iac/providers/google/sql/sql.go @@ -66,6 +66,7 @@ type Backups struct { type IPConfiguration struct { Metadata iacTypes.Metadata RequireTLS iacTypes.BoolValue + SSLMode iacTypes.StringValue EnableIPv4 iacTypes.BoolValue AuthorizedNetworks []struct { Name iacTypes.StringValue diff --git a/pkg/iac/rego/schemas/cloud.json b/pkg/iac/rego/schemas/cloud.json index d0ce7ffe515a..3cc6301429be 100644 --- a/pkg/iac/rego/schemas/cloud.json +++ b/pkg/iac/rego/schemas/cloud.json @@ -6991,6 +6991,10 @@ "requiretls": { "type": "object", "$ref": "#/definitions/github.aaakk.us.kg.aquasecurity.trivy.pkg.iac.types.BoolValue" + }, + "sslmode": { + "type": "object", + "$ref": "#/definitions/github.aaakk.us.kg.aquasecurity.trivy.pkg.iac.types.StringValue" } } }, From 9da84f54fadbe6ad0d73983952e945ed63b666f3 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Thu, 17 Oct 2024 09:39:58 +0600 Subject: [PATCH 037/108] fix(misconf): change default ACL of digitalocean_spaces_bucket to private (#7577) Signed-off-by: nikpivkin --- pkg/iac/adapters/terraform/digitalocean/spaces/adapt.go | 2 +- pkg/iac/adapters/terraform/digitalocean/spaces/adapt_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/iac/adapters/terraform/digitalocean/spaces/adapt.go b/pkg/iac/adapters/terraform/digitalocean/spaces/adapt.go index 46d1f25fa386..a1a1c792d647 100644 --- a/pkg/iac/adapters/terraform/digitalocean/spaces/adapt.go +++ b/pkg/iac/adapters/terraform/digitalocean/spaces/adapt.go @@ -24,7 +24,7 @@ func adaptBuckets(modules terraform.Modules) []spaces.Bucket { Metadata: block.GetMetadata(), Name: block.GetAttribute("name").AsStringValueOrDefault("", block), Objects: nil, - ACL: block.GetAttribute("acl").AsStringValueOrDefault("public-read", block), + ACL: block.GetAttribute("acl").AsStringValueOrDefault("private", block), ForceDestroy: block.GetAttribute("force_destroy").AsBoolValueOrDefault(false, block), Versioning: spaces.Versioning{ Metadata: block.GetMetadata(), diff --git a/pkg/iac/adapters/terraform/digitalocean/spaces/adapt_test.go b/pkg/iac/adapters/terraform/digitalocean/spaces/adapt_test.go index cf3fc84e4ff7..e87987a3f473 100644 --- a/pkg/iac/adapters/terraform/digitalocean/spaces/adapt_test.go +++ b/pkg/iac/adapters/terraform/digitalocean/spaces/adapt_test.go @@ -69,7 +69,7 @@ func Test_adaptBuckets(t *testing.T) { Metadata: iacTypes.NewTestMetadata(), Name: iacTypes.String("", iacTypes.NewTestMetadata()), Objects: nil, - ACL: iacTypes.String("public-read", iacTypes.NewTestMetadata()), + ACL: iacTypes.String("private", iacTypes.NewTestMetadata()), ForceDestroy: iacTypes.Bool(false, iacTypes.NewTestMetadata()), Versioning: spaces.Versioning{ Metadata: iacTypes.NewTestMetadata(), From c8c14d36245623019f29d258f813d2325f7490f7 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Thu, 17 Oct 2024 09:40:14 +0600 Subject: [PATCH 038/108] fix(misconf): check if property is not nil before conversion (#7578) Signed-off-by: nikpivkin --- .../cloudformation/aws/ec2/adapt_test.go | 28 +++++++++++++++++++ .../parser/property_conversion.go | 7 +++++ 2 files changed, 35 insertions(+) diff --git a/pkg/iac/adapters/cloudformation/aws/ec2/adapt_test.go b/pkg/iac/adapters/cloudformation/aws/ec2/adapt_test.go index b218b95a4e99..c56471aa2bad 100644 --- a/pkg/iac/adapters/cloudformation/aws/ec2/adapt_test.go +++ b/pkg/iac/adapters/cloudformation/aws/ec2/adapt_test.go @@ -338,6 +338,34 @@ Resources: }, }, }, + { + name: "empty", + source: `--- +AWSTemplateFormatVersion: 2010-09-09 +Description: Godd example of excessive ports +Resources: + NetworkACL: + Type: AWS::EC2::NetworkAcl + Rule: + Type: AWS::EC2::NetworkAclEntry + Properties: + NetworkAclId: + Ref: NetworkACL`, + expected: ec2.EC2{ + NetworkACLs: []ec2.NetworkACL{ + { + Rules: []ec2.NetworkACLRule{ + { + Action: types.StringTest("allow"), + Type: types.StringTest("ingress"), + FromPort: types.IntTest(-1), + ToPort: types.IntTest(-1), + }, + }, + }, + }, + }, + }, } for _, tt := range tests { diff --git a/pkg/iac/scanners/cloudformation/parser/property_conversion.go b/pkg/iac/scanners/cloudformation/parser/property_conversion.go index 35847bb35c5b..d1ef23d396f2 100644 --- a/pkg/iac/scanners/cloudformation/parser/property_conversion.go +++ b/pkg/iac/scanners/cloudformation/parser/property_conversion.go @@ -10,6 +10,10 @@ import ( ) func (p *Property) IsConvertableTo(conversionType cftypes.CfType) bool { + if p.IsNil() { + return false + } + switch conversionType { case cftypes.Int: return p.isConvertableToInt() @@ -62,6 +66,9 @@ func (p *Property) isConvertableToInt() bool { } func (p *Property) ConvertTo(conversionType cftypes.CfType) *Property { + if p.IsNil() { + return nil + } if p.Type() == conversionType { return p From 778df828eaad9827cb833c6285058a33aa2b83ca Mon Sep 17 00:00:00 2001 From: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> Date: Thu, 17 Oct 2024 12:54:12 +0600 Subject: [PATCH 039/108] fix(java): correctly inherit `version` and `scope` from upper/root `depManagement` and `dependencies` into parents (#7541) Signed-off-by: knqyf263 Co-authored-by: knqyf263 --- pkg/dependency/parser/java/pom/parse.go | 147 ++++---- pkg/dependency/parser/java/pom/parse_test.go | 344 ++++++++++++++++++ pkg/dependency/parser/java/pom/pom.go | 29 +- .../pom.xml | 29 ++ .../pom.xml | 64 ++++ .../parent/pom.xml | 77 ++++ .../pom.xml | 39 ++ .../example-nested-scope-compile-1.0.0.pom | 41 +++ .../1.0.0/parent/pom.xml | 21 ++ .../example-nested-scope-empty-1.0.0.pom | 40 ++ .../1.0.0/parent/pom.xml | 21 ++ .../example-nested-scope-runtime-1.0.0.pom | 41 +++ .../1.0.0/parent/pom.xml | 21 ++ .../2.0.0/example-scope-compile-2.0.0.pom | 23 ++ .../2.0.0/example-scope-empty-2.0.0.pom | 22 ++ .../2.0.0/example-scope-runtime-2.0.0.pom | 23 ++ .../testdata/transitive-parents/base/pom.xml | 2 +- .../parent/pom.xml | 34 ++ .../pom.xml | 45 +++ .../top-parent/pom.xml | 34 ++ 20 files changed, 1013 insertions(+), 84 deletions(-) create mode 100644 pkg/dependency/parser/java/pom/testdata/inherit-scopes-from-child-deps-and-their-parents/pom.xml create mode 100644 pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-children-from-root/pom.xml create mode 100644 pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-parents-from-root/parent/pom.xml create mode 100644 pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-parents-from-root/pom.xml create mode 100644 pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-compile/1.0.0/example-nested-scope-compile-1.0.0.pom create mode 100644 pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-compile/1.0.0/parent/pom.xml create mode 100644 pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-empty/1.0.0/example-nested-scope-empty-1.0.0.pom create mode 100644 pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-empty/1.0.0/parent/pom.xml create mode 100644 pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-runtime/1.0.0/example-nested-scope-runtime-1.0.0.pom create mode 100644 pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-runtime/1.0.0/parent/pom.xml create mode 100644 pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-compile/2.0.0/example-scope-compile-2.0.0.pom create mode 100644 pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-empty/2.0.0/example-scope-empty-2.0.0.pom create mode 100644 pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-runtime/2.0.0/example-scope-runtime-2.0.0.pom create mode 100644 pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/parent/pom.xml create mode 100644 pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/pom.xml create mode 100644 pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/top-parent/pom.xml diff --git a/pkg/dependency/parser/java/pom/parse.go b/pkg/dependency/parser/java/pom/parse.go index 542abcac3c8a..2c8a194ea50b 100644 --- a/pkg/dependency/parser/java/pom/parse.go +++ b/pkg/dependency/parser/java/pom/parse.go @@ -96,7 +96,7 @@ func NewParser(filePath string, opts ...option) *Parser { } func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency, error) { - content, err := parsePom(r) + content, err := parsePom(r, true) if err != nil { return nil, nil, xerrors.Errorf("failed to parse POM: %w", err) } @@ -107,7 +107,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc } // Analyze root POM - result, err := p.analyze(root, analysisOptions{lineNumber: true}) + result, err := p.analyze(root, analysisOptions{}) if err != nil { return nil, nil, xerrors.Errorf("analyze error (%s): %w", p.rootPath, err) } @@ -330,53 +330,27 @@ type analysisResult struct { type analysisOptions struct { exclusions map[string]struct{} depManagement []pomDependency // from the root POM - lineNumber bool // Save line numbers } func (p *Parser) analyze(pom *pom, opts analysisOptions) (analysisResult, error) { - if pom == nil || pom.content == nil { + if pom.nil() { return analysisResult{}, nil } - // Update remoteRepositories pomReleaseRemoteRepos, pomSnapshotRemoteRepos := pom.repositories(p.servers) p.releaseRemoteRepos = lo.Uniq(append(pomReleaseRemoteRepos, p.releaseRemoteRepos...)) p.snapshotRemoteRepos = lo.Uniq(append(pomSnapshotRemoteRepos, p.snapshotRemoteRepos...)) - // We need to forward dependencyManagements from current and root pom to Parent, - // to use them for dependencies in parent. - // For better understanding see the following tests: - // - `dependency from parent uses version from child pom depManagement` - // - `dependency from parent uses version from root pom depManagement` - // - // depManagements from root pom has higher priority than depManagements from current pom. - depManagementForParent := lo.UniqBy(append(opts.depManagement, pom.content.DependencyManagement.Dependencies.Dependency...), - func(dep pomDependency) string { - return dep.Name() - }) - - // Parent - parent, err := p.parseParent(pom.filePath, pom.content.Parent, depManagementForParent) - if err != nil { - return analysisResult{}, xerrors.Errorf("parent error: %w", err) + // Resolve parent POM + if err := p.resolveParent(pom); err != nil { + return analysisResult{}, xerrors.Errorf("pom resolve error: %w", err) } - // Inherit values/properties from parent - pom.inherit(parent) - - // Generate properties + // Resolve dependencies props := pom.properties() - - // dependencyManagements have the next priority: - // 1. Managed dependencies from this POM - // 2. Managed dependencies from parent of this POM - depManagement := p.mergeDependencyManagements(pom.content.DependencyManagement.Dependencies.Dependency, - parent.dependencyManagement) - - // Merge dependencies. Child dependencies must be preferred than parent dependencies. - // Parents don't have to resolve dependencies. + depManagement := pom.content.DependencyManagement.Dependencies.Dependency deps := p.parseDependencies(pom.content.Dependencies.Dependency, props, depManagement, opts) - deps = p.mergeDependencies(parent.dependencies, deps, opts.exclusions) + deps = p.filterDependencies(deps, opts.exclusions) return analysisResult{ filePath: pom.filePath, @@ -388,6 +362,39 @@ func (p *Parser) analyze(pom *pom, opts analysisOptions) (analysisResult, error) }, nil } +// resolveParent resolves its parent POMs and inherits properties, dependencies, and dependencyManagement. +func (p *Parser) resolveParent(pom *pom) error { + if pom.nil() { + return nil + } + + // Parse parent POM + parent, err := p.parseParent(pom.filePath, pom.content.Parent) + if err != nil { + return xerrors.Errorf("parent error: %w", err) + } + + // Inherit values/properties from parent + pom.inherit(parent) + + // Merge properties + pom.content.Properties = p.mergeProperties(pom.content.Properties, parent.content.Properties) + + // Merge dependencyManagement with the following priority: + // 1. Managed dependencies from this POM + // 2. Managed dependencies from parent of this POM + pom.content.DependencyManagement.Dependencies.Dependency = p.mergeDependencyManagements( + pom.content.DependencyManagement.Dependencies.Dependency, + parent.content.DependencyManagement.Dependencies.Dependency) + + // Merge dependencies + pom.content.Dependencies.Dependency = p.mergeDependencies( + pom.content.Dependencies.Dependency, + parent.content.Dependencies.Dependency) + + return nil +} + func (p *Parser) mergeDependencyManagements(depManagements ...[]pomDependency) []pomDependency { uniq := make(map[string]struct{}) var depManagement []pomDependency @@ -463,22 +470,20 @@ func (p *Parser) resolveDepManagement(props map[string]string, depManagement []p return newDepManagement } -func (p *Parser) mergeDependencies(parent, child []artifact, exclusions map[string]struct{}) []artifact { - var deps []artifact - unique := make(map[string]struct{}) +func (p *Parser) mergeProperties(child, parent properties) properties { + return lo.Assign(parent, child) +} - for _, d := range append(child, parent...) { - if excludeDep(exclusions, d) { - continue - } - if _, ok := unique[d.Name()]; ok { - continue - } - unique[d.Name()] = struct{}{} - deps = append(deps, d) - } +func (p *Parser) mergeDependencies(child, parent []pomDependency) []pomDependency { + return lo.UniqBy(append(child, parent...), func(d pomDependency) string { + return d.Name() + }) +} - return deps +func (p *Parser) filterDependencies(artifacts []artifact, exclusions map[string]struct{}) []artifact { + return lo.Filter(artifacts, func(art artifact, _ int) bool { + return !excludeDep(exclusions, art) + }) } func excludeDep(exclusions map[string]struct{}, art artifact) bool { @@ -497,38 +502,29 @@ func excludeDep(exclusions map[string]struct{}, art artifact) bool { return false } -func (p *Parser) parseParent(currentPath string, parent pomParent, rootDepManagement []pomDependency) (analysisResult, error) { +func (p *Parser) parseParent(currentPath string, parent pomParent) (*pom, error) { // Pass nil properties so that variables in are not evaluated. target := newArtifact(parent.GroupId, parent.ArtifactId, parent.Version, nil, nil) // if version is property (e.g. ${revision}) - we still need to parse this pom if target.IsEmpty() && !isProperty(parent.Version) { - return analysisResult{}, nil + return &pom{content: &pomXML{}}, nil } logger := p.logger.With("artifact", target.String()) logger.Debug("Start parent") defer logger.Debug("Exit parent") - // If the artifact is found in cache, it is returned. - if result := p.cache.get(target); result != nil { - return *result, nil - } - parentPOM, err := p.retrieveParent(currentPath, parent.RelativePath, target) if err != nil { logger.Debug("Parent POM not found", log.Err(err)) + return &pom{content: &pomXML{}}, nil } - result, err := p.analyze(parentPOM, analysisOptions{ - depManagement: rootDepManagement, - }) - if err != nil { - return analysisResult{}, xerrors.Errorf("analyze error: %w", err) + if err = p.resolveParent(parentPOM); err != nil { + return nil, xerrors.Errorf("parent pom resolve error: %w", err) } - p.cache.put(target, result) - - return result, nil + return parentPOM, nil } func (p *Parser) retrieveParent(currentPath, relativePath string, target artifact) (*pom, error) { @@ -565,7 +561,7 @@ func (p *Parser) retrieveParent(currentPath, relativePath string, target artifac } func (p *Parser) tryRelativePath(parentArtifact artifact, currentPath, relativePath string) (*pom, error) { - pom, err := p.openRelativePom(currentPath, relativePath) + parsedPOM, err := p.openRelativePom(currentPath, relativePath) if err != nil { return nil, err } @@ -576,19 +572,18 @@ func (p *Parser) tryRelativePath(parentArtifact artifact, currentPath, relativeP // But GroupID can be inherited from parent (`p.analyze` function is required to get the GroupID). // Version can contain a property (`p.analyze` function is required to get the GroupID). // So we can only match ArtifactID's. - if pom.artifact().ArtifactID != parentArtifact.ArtifactID { + if parsedPOM.artifact().ArtifactID != parentArtifact.ArtifactID { return nil, xerrors.New("'parent.relativePath' points at wrong local POM") } - result, err := p.analyze(pom, analysisOptions{}) - if err != nil { + if err := p.resolveParent(parsedPOM); err != nil { return nil, xerrors.Errorf("analyze error: %w", err) } - if !parentArtifact.Equal(result.artifact) { + if !parentArtifact.Equal(parsedPOM.artifact()) { return nil, xerrors.New("'parent.relativePath' points at wrong local POM") } - return pom, nil + return parsedPOM, nil } func (p *Parser) openRelativePom(currentPath, relativePath string) (*pom, error) { @@ -620,7 +615,7 @@ func (p *Parser) openPom(filePath string) (*pom, error) { } defer f.Close() - content, err := parsePom(f) + content, err := parsePom(f, false) if err != nil { return nil, xerrors.Errorf("failed to parse the local POM: %w", err) } @@ -777,7 +772,7 @@ func (p *Parser) fetchPOMFromRemoteRepository(repo string, paths []string) (*pom } defer resp.Body.Close() - content, err := parsePom(resp.Body) + content, err := parsePom(resp.Body, false) if err != nil { return nil, xerrors.Errorf("failed to parse the remote POM: %w", err) } @@ -788,13 +783,19 @@ func (p *Parser) fetchPOMFromRemoteRepository(repo string, paths []string) (*pom }, nil } -func parsePom(r io.Reader) (*pomXML, error) { +func parsePom(r io.Reader, lineNumber bool) (*pomXML, error) { parsed := &pomXML{} decoder := xml.NewDecoder(r) decoder.CharsetReader = charset.NewReaderLabel if err := decoder.Decode(parsed); err != nil { return nil, xerrors.Errorf("xml decode error: %w", err) } + if !lineNumber { + for i := range parsed.Dependencies.Dependency { + parsed.Dependencies.Dependency[i].StartLine = 0 + parsed.Dependencies.Dependency[i].EndLine = 0 + } + } return parsed, nil } diff --git a/pkg/dependency/parser/java/pom/parse_test.go b/pkg/dependency/parser/java/pom/parse_test.go index 82604846a4f3..7b77c0a7ef5e 100644 --- a/pkg/dependency/parser/java/pom/parse_test.go +++ b/pkg/dependency/parser/java/pom/parse_test.go @@ -14,6 +14,100 @@ import ( ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" ) +var ( + exampleNestedScopeCompile = func(start, end int) ftypes.Package { + var location ftypes.Locations + if start != 0 && end != 0 { + location = append(location, ftypes.Location{ + StartLine: start, + EndLine: end, + }) + } + return ftypes.Package{ + ID: "org.example:example-nested-scope-compile:1.0.0", + Name: "org.example:example-nested-scope-compile", + Version: "1.0.0", + Relationship: ftypes.RelationshipDirect, + Locations: location, + } + } + + exampleNestedScopeEmpty = func(start, end int) ftypes.Package { + var location ftypes.Locations + if start != 0 && end != 0 { + location = append(location, ftypes.Location{ + StartLine: start, + EndLine: end, + }) + } + return ftypes.Package{ + ID: "org.example:example-nested-scope-empty:1.0.0", + Name: "org.example:example-nested-scope-empty", + Version: "1.0.0", + Relationship: ftypes.RelationshipDirect, + Locations: location, + } + } + + exampleNestedScopeRuntime = func(start, end int) ftypes.Package { + var location ftypes.Locations + if start != 0 && end != 0 { + location = append(location, ftypes.Location{ + StartLine: start, + EndLine: end, + }) + } + return ftypes.Package{ + ID: "org.example:example-nested-scope-runtime:1.0.0", + Name: "org.example:example-nested-scope-runtime", + Version: "1.0.0", + Relationship: ftypes.RelationshipDirect, + Locations: location, + } + } + + exampleScopeCompile = ftypes.Package{ + ID: "org.example:example-scope-compile:2.0.0", + Name: "org.example:example-scope-compile", + Version: "2.0.0", + Relationship: ftypes.RelationshipIndirect, + } + + exampleScopeEmpty = ftypes.Package{ + ID: "org.example:example-scope-empty:2.0.0", + Name: "org.example:example-scope-empty", + Version: "2.0.0", + Relationship: ftypes.RelationshipIndirect, + } + + exampleScopeRuntime = ftypes.Package{ + ID: "org.example:example-scope-runtime:2.0.0", + Name: "org.example:example-scope-runtime", + Version: "2.0.0", + Relationship: ftypes.RelationshipIndirect, + } + exampleApiCompile = ftypes.Package{ + ID: "org.example:example-api-compile:3.0.0", + Name: "org.example:example-api-compile", + Version: "3.0.0", + Relationship: ftypes.RelationshipIndirect, + } + + exampleApiEmpty = ftypes.Package{ + ID: "org.example:example-api-empty:3.0.0", + Name: "org.example:example-api-empty", + Version: "3.0.0", + Relationship: ftypes.RelationshipIndirect, + } + + exampleApiRuntime = ftypes.Package{ + ID: "org.example:example-api-runtime:3.0.0", + Name: "org.example:example-api-runtime", + Version: "3.0.0", + Relationship: ftypes.RelationshipIndirect, + } +) + func TestPom_Parse(t *testing.T) { tests := []struct { name string @@ -1630,6 +1724,256 @@ func TestPom_Parse(t *testing.T) { }, }, }, + // [INFO] com.example:child-depManagement-in-parent:jar:1.0.0 + // [INFO] +- org.example:example-api2:jar:1.0.2:runtime + // [INFO] +- org.example:example-api3:jar:4.0.3:compile + // [INFO] \- org.example:example-api:jar:1.0.1:compile + { + name: "dependency from parent uses version from child(scanned) pom depManagement", + inputFile: filepath.Join("testdata", "use-child-dep-management-in-parent", "pom.xml"), + local: true, + want: []ftypes.Package{ + { + ID: "com.example:child-depManagement-in-parent:1.0.0", + Name: "com.example:child-depManagement-in-parent", + Version: "1.0.0", + Relationship: ftypes.RelationshipRoot, + }, + { + ID: "org.example:example-api:1.0.1", + Name: "org.example:example-api", + Version: "1.0.1", + Relationship: ftypes.RelationshipDirect, + }, + { + ID: "org.example:example-api2:1.0.2", + Name: "org.example:example-api2", + Version: "1.0.2", + Relationship: ftypes.RelationshipDirect, + }, + { + ID: "org.example:example-api3:4.0.3", + Name: "org.example:example-api3", + Version: "4.0.3", + Relationship: ftypes.RelationshipDirect, + }, + }, + wantDeps: []ftypes.Dependency{ + { + ID: "com.example:child-depManagement-in-parent:1.0.0", + DependsOn: []string{ + "org.example:example-api2:1.0.2", + "org.example:example-api3:4.0.3", + "org.example:example-api:1.0.1", + }, + }, + }, + }, + // [INFO] com.example:inherit-scopes-from-child-deps-and-their-parents:jar:0.0.1 + // [INFO] +- org.example:example-nested-scope-runtime:jar:1.0.0:runtime + // [INFO] | \- org.example:example-scope-runtime:jar:2.0.0:runtime + // [INFO] | \- org.example:example-api-runtime:jar:3.0.0:runtime + // [INFO] +- org.example:example-nested-scope-compile:jar:1.0.0:compile + // [INFO] | \- org.example:example-scope-compile:jar:2.0.0:compile + // [INFO] | \- org.example:example-api-compile:jar:3.0.0:compile + // [INFO] \- org.example:example-nested-scope-empty:jar:1.0.0:compile + // [INFO] \- org.example:example-scope-empty:jar:2.0.0:compile + // [INFO] \- org.example:example-api-empty:jar:3.0.0:compile + // + // `example-nested-*" dependencies and their parents contain `dependencyManagement` with changed scopes + { + name: "inherit scopes from child dependencies and their parents", + inputFile: filepath.Join("testdata", "inherit-scopes-from-child-deps-and-their-parents", "pom.xml"), + local: true, + want: []ftypes.Package{ + { + ID: "com.example:inherit-scopes-from-child-deps-and-their-parents:0.0.1", + Name: "com.example:inherit-scopes-from-child-deps-and-their-parents", + Version: "0.0.1", + Relationship: ftypes.RelationshipRoot, + }, + exampleNestedScopeCompile(16, 21), + exampleNestedScopeEmpty(22, 26), + exampleNestedScopeRuntime(10, 15), + exampleApiCompile, + exampleApiEmpty, + exampleApiRuntime, + exampleScopeCompile, + exampleScopeEmpty, + exampleScopeRuntime, + }, + wantDeps: []ftypes.Dependency{ + { + ID: "com.example:inherit-scopes-from-child-deps-and-their-parents:0.0.1", + DependsOn: []string{ + "org.example:example-nested-scope-compile:1.0.0", + "org.example:example-nested-scope-empty:1.0.0", + "org.example:example-nested-scope-runtime:1.0.0", + }, + }, + { + ID: "org.example:example-nested-scope-compile:1.0.0", + DependsOn: []string{ + "org.example:example-scope-compile:2.0.0", + }, + }, + { + ID: "org.example:example-nested-scope-empty:1.0.0", + DependsOn: []string{ + "org.example:example-scope-empty:2.0.0", + }, + }, + { + ID: "org.example:example-nested-scope-runtime:1.0.0", + DependsOn: []string{ + "org.example:example-scope-runtime:2.0.0", + }, + }, + { + ID: "org.example:example-scope-compile:2.0.0", + DependsOn: []string{ + "org.example:example-api-compile:3.0.0", + }, + }, + { + ID: "org.example:example-scope-empty:2.0.0", + DependsOn: []string{ + "org.example:example-api-empty:3.0.0", + }, + }, + { + ID: "org.example:example-scope-runtime:2.0.0", + DependsOn: []string{ + "org.example:example-api-runtime:3.0.0", + }, + }, + }, + }, + // [INFO] com.example:inherit-scopes-in-parents-from-root:jar:0.1.0 + // [INFO] +- org.example:example-nested-scope-runtime:jar:1.0.0:runtime + // [INFO] | \- org.example:example-scope-runtime:jar:2.0.0:compile + // [INFO] | \- org.example:example-api-runtime:jar:3.0.0:runtime + // [INFO] +- org.example:example-nested-scope-compile:jar:1.0.0:compile + // [INFO] | \- org.example:example-scope-compile:jar:2.0.0:runtime + // [INFO] | \- org.example:example-api-compile:jar:3.0.0:test + // [INFO] \- org.example:example-nested-scope-empty:jar:1.0.0:compile + // [INFO] \- org.example:example-scope-empty:jar:2.0.0:runtime + // [INFO] \- org.example:example-api-empty:jar:3.0.0:test + // + // `example-nested-*" dependencies and their parents contain `dependencyManagement` with changed scopes + // scopes from `dependencyManagement` of root pom are used + { + name: "inherit scopes in children from root pom", + inputFile: filepath.Join("testdata", "inherit-scopes-in-children-from-root", "pom.xml"), + local: true, + want: []ftypes.Package{ + { + ID: "com.example:inherit-scopes-in-children-from-root:0.0.1", + Name: "com.example:inherit-scopes-in-children-from-root", + Version: "0.0.1", + Relationship: ftypes.RelationshipRoot, + }, + exampleNestedScopeCompile(51, 56), + exampleNestedScopeEmpty(57, 61), + exampleNestedScopeRuntime(45, 50), + exampleApiRuntime, + exampleScopeCompile, + exampleScopeEmpty, + exampleScopeRuntime, + }, + wantDeps: []ftypes.Dependency{ + { + ID: "com.example:inherit-scopes-in-children-from-root:0.0.1", + DependsOn: []string{ + "org.example:example-nested-scope-compile:1.0.0", + "org.example:example-nested-scope-empty:1.0.0", + "org.example:example-nested-scope-runtime:1.0.0", + }, + }, + { + ID: "org.example:example-nested-scope-compile:1.0.0", + DependsOn: []string{ + "org.example:example-scope-compile:2.0.0", + }, + }, + { + ID: "org.example:example-nested-scope-empty:1.0.0", + DependsOn: []string{ + "org.example:example-scope-empty:2.0.0", + }, + }, + { + ID: "org.example:example-nested-scope-runtime:1.0.0", + DependsOn: []string{ + "org.example:example-scope-runtime:2.0.0", + }, + }, + { + ID: "org.example:example-scope-runtime:2.0.0", + DependsOn: []string{ + "org.example:example-api-runtime:3.0.0", + }, + }, + }, + }, + // [INFO] com.example:inherit-scopes-in-parents-from-root:jar:0.1.0 + // [INFO] +- org.example:example-nested-scope-runtime:jar:1.0.0:runtime + // [INFO] | \- org.example:example-scope-runtime:jar:2.0.0:compile + // [INFO] | \- org.example:example-api-runtime:jar:3.0.0:runtime + // [INFO] +- org.example:example-nested-scope-compile:jar:1.0.0:compile + // [INFO] | \- org.example:example-scope-compile:jar:2.0.0:runtime + // [INFO] | \- org.example:example-api-compile:jar:3.0.0:test + // [INFO] \- org.example:example-nested-scope-empty:jar:1.0.0:test + // [INFO] \- org.example:example-scope-empty:jar:2.0.0:test + // [INFO] \- org.example:example-api-empty:jar:3.0.0:test + // + // `example-nested-*" dependencies and their parents contain `dependencyManagement` with changed scopes + // scopes from `dependencyManagement` of root pom are used in parent dependencies + { + name: "inherit scopes in parent from root pom", + inputFile: filepath.Join("testdata", "inherit-scopes-in-parents-from-root", "pom.xml"), + local: true, + want: []ftypes.Package{ + { + ID: "com.example:inherit-scopes-in-parents-from-root:0.1.0", + Name: "com.example:inherit-scopes-in-parents-from-root", + Version: "0.1.0", + Relationship: ftypes.RelationshipRoot, + }, + exampleNestedScopeCompile(0, 0), + exampleNestedScopeRuntime(0, 0), + exampleApiRuntime, + exampleScopeCompile, + exampleScopeRuntime, + }, + wantDeps: []ftypes.Dependency{ + { + ID: "com.example:inherit-scopes-in-parents-from-root:0.1.0", + DependsOn: []string{ + "org.example:example-nested-scope-compile:1.0.0", + "org.example:example-nested-scope-runtime:1.0.0", + }, + }, + { + ID: "org.example:example-nested-scope-compile:1.0.0", + DependsOn: []string{ + "org.example:example-scope-compile:2.0.0", + }, + }, + { + ID: "org.example:example-nested-scope-runtime:1.0.0", + DependsOn: []string{ + "org.example:example-scope-runtime:2.0.0", + }, + }, + { + ID: "org.example:example-scope-runtime:2.0.0", + DependsOn: []string{ + "org.example:example-api-runtime:3.0.0", + }, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/dependency/parser/java/pom/pom.go b/pkg/dependency/parser/java/pom/pom.go index 889d107c3c6c..83c5d4fec609 100644 --- a/pkg/dependency/parser/java/pom/pom.go +++ b/pkg/dependency/parser/java/pom/pom.go @@ -23,11 +23,18 @@ type pom struct { content *pomXML } -func (p *pom) inherit(result analysisResult) { +func (p *pom) nil() bool { + return p == nil || p.content == nil +} + +func (p *pom) inherit(parent *pom) { + if parent == nil { + return + } // Merge properties - p.content.Properties = utils.MergeMaps(result.properties, p.content.Properties) + p.content.Properties = utils.MergeMaps(parent.properties(), p.content.Properties) - art := p.artifact().Inherit(result.artifact) + art := p.artifact().Inherit(parent.artifact()) p.content.GroupId = art.GroupID p.content.ArtifactId = art.ArtifactID @@ -40,12 +47,12 @@ func (p *pom) inherit(result analysisResult) { } } -func (p pom) properties() properties { +func (p *pom) properties() properties { props := p.content.Properties return utils.MergeMaps(props, p.projectProperties()) } -func (p pom) projectProperties() map[string]string { +func (p *pom) projectProperties() map[string]string { val := reflect.ValueOf(p.content).Elem() props := p.listProperties(val) @@ -73,7 +80,7 @@ func (p pom) projectProperties() map[string]string { return projectProperties } -func (p pom) listProperties(val reflect.Value) map[string]string { +func (p *pom) listProperties(val reflect.Value) map[string]string { props := make(map[string]string) for i := 0; i < val.NumField(); i++ { f := val.Type().Field(i) @@ -106,17 +113,17 @@ func (p pom) listProperties(val reflect.Value) map[string]string { return props } -func (p pom) artifact() artifact { +func (p *pom) artifact() artifact { return newArtifact(p.content.GroupId, p.content.ArtifactId, p.content.Version, p.licenses(), p.content.Properties) } -func (p pom) licenses() []string { +func (p *pom) licenses() []string { return slices.ZeroToNil(lo.FilterMap(p.content.Licenses.License, func(lic pomLicense, _ int) (string, bool) { return lic.Name, lic.Name != "" })) } -func (p pom) repositories(servers []Server) ([]string, []string) { +func (p *pom) repositories(servers []Server) ([]string, []string) { logger := log.WithPrefix("pom") var releaseRepos, snapshotRepos []string for _, rep := range p.content.Repositories.Repository { @@ -242,9 +249,11 @@ func (d pomDependency) Resolve(props map[string]string, depManagement, rootDepMa if managed.Version != "" { dep.Version = evaluateVariable(managed.Version, props, nil) } + if managed.Scope != "" { dep.Scope = evaluateVariable(managed.Scope, props, nil) } + if managed.Optional { dep.Optional = managed.Optional } @@ -287,7 +296,7 @@ func (d pomDependency) ToArtifact(opts analysisOptions) artifact { } var locations ftypes.Locations - if opts.lineNumber { + if d.StartLine != 0 && d.EndLine != 0 { locations = ftypes.Locations{ { StartLine: d.StartLine, diff --git a/pkg/dependency/parser/java/pom/testdata/inherit-scopes-from-child-deps-and-their-parents/pom.xml b/pkg/dependency/parser/java/pom/testdata/inherit-scopes-from-child-deps-and-their-parents/pom.xml new file mode 100644 index 000000000000..17e6c55de090 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/inherit-scopes-from-child-deps-and-their-parents/pom.xml @@ -0,0 +1,29 @@ + + 4.0.0 + + com.example + inherit-scopes-from-child-deps-and-their-parents + 0.0.1 + + + + org.example + example-nested-scope-runtime + 1.0.0 + runtime + + + org.example + example-nested-scope-compile + 1.0.0 + compile + + + org.example + example-nested-scope-empty + 1.0.0 + + + + diff --git a/pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-children-from-root/pom.xml b/pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-children-from-root/pom.xml new file mode 100644 index 000000000000..26bdb25621d7 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-children-from-root/pom.xml @@ -0,0 +1,64 @@ + + 4.0.0 + + com.example + inherit-scopes-in-children-from-root + 0.0.1 + + + + + org.example + example-scope-runtime + 2.0.0 + compile + + + org.example + example-scope-compile + 2.0.0 + runtime + + + org.example + example-api-compile + 3.0.0 + test + + + org.example + example-scope-empty + 2.0.0 + runtime + + + org.example + example-api-empty + 3.0.0 + test + + + + + + + org.example + example-nested-scope-runtime + 1.0.0 + runtime + + + org.example + example-nested-scope-compile + 1.0.0 + compile + + + org.example + example-nested-scope-empty + 1.0.0 + + + + diff --git a/pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-parents-from-root/parent/pom.xml b/pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-parents-from-root/parent/pom.xml new file mode 100644 index 000000000000..dad0101e3f25 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-parents-from-root/parent/pom.xml @@ -0,0 +1,77 @@ + + 4.0.0 + + com.example + inherit-scopes-in-parents-from-root-parent + 0.0.1 + pom + + + + + org.example + example-scope-runtime + 2.0.0 + compile + + + org.example + example-scope-compile + 2.0.0 + runtime + + + org.example + example-api-compile + 3.0.0 + test + + + org.example + example-scope-empty + 2.0.0 + test + + + org.example + example-api-empty + 3.0.0 + test + + + org.example + example-nested-scope-compile + 1.0.0 + runtime + + + org.example + example-nested-scope-empty + 1.0.0 + compile + + + + + + + org.example + example-nested-scope-runtime + 1.0.0 + runtime + + + org.example + example-nested-scope-compile + 1.0.0 + compile + + + org.example + example-nested-scope-empty + 1.0.0 + + + + diff --git a/pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-parents-from-root/pom.xml b/pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-parents-from-root/pom.xml new file mode 100644 index 000000000000..45ef37785e60 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/inherit-scopes-in-parents-from-root/pom.xml @@ -0,0 +1,39 @@ + + 4.0.0 + + com.example + inherit-scopes-in-parents-from-root + 0.1.0 + + + com.example + inherit-scopes-in-parents-from-root-parent + 0.0.1 + ./parent + + + + + + org.example + example-nested-scope-runtime + 1.0.0 + test + + + org.example + example-nested-scope-compile + 1.0.0 + test + + + org.example + example-nested-scope-empty + 1.0.0 + test + + + + + diff --git a/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-compile/1.0.0/example-nested-scope-compile-1.0.0.pom b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-compile/1.0.0/example-nested-scope-compile-1.0.0.pom new file mode 100644 index 000000000000..43ca478a88cd --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-compile/1.0.0/example-nested-scope-compile-1.0.0.pom @@ -0,0 +1,41 @@ + + + + 4.0.0 + + + com.example + example-nested-parent-scope-compile + 1.0.1 + ./parent + + + org.example + example-nested-scope-compile + 1.0.0 + + jar + Example pom with example-scope-compile 2.0.0 + + + + + org.example + example-api-compile + 3.0.0 + runtime + + + + + + + org.example + example-scope-compile + 2.0.0 + compile + + + \ No newline at end of file diff --git a/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-compile/1.0.0/parent/pom.xml b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-compile/1.0.0/parent/pom.xml new file mode 100644 index 000000000000..5fe41b350df1 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-compile/1.0.0/parent/pom.xml @@ -0,0 +1,21 @@ + + 4.0.0 + + com.example + example-nested-parent-scope-compile + 1.0.1 + pom + + + + + org.example + example-scope-compile + 2.0.0 + test + + + + + diff --git a/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-empty/1.0.0/example-nested-scope-empty-1.0.0.pom b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-empty/1.0.0/example-nested-scope-empty-1.0.0.pom new file mode 100644 index 000000000000..b1def2383f79 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-empty/1.0.0/example-nested-scope-empty-1.0.0.pom @@ -0,0 +1,40 @@ + + + + 4.0.0 + + + com.example + example-nested-parent-scope-empty + 1.0.1 + ./parent + + + org.example + example-nested-scope-empty + 1.0.0 + + jar + Example pom with example-scope-empty 2.0.0 + + + + + org.example + example-api-empty + 3.0.0 + test + + + + + + + org.example + example-scope-empty + 2.0.0 + + + \ No newline at end of file diff --git a/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-empty/1.0.0/parent/pom.xml b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-empty/1.0.0/parent/pom.xml new file mode 100644 index 000000000000..6ca0232f4fc3 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-empty/1.0.0/parent/pom.xml @@ -0,0 +1,21 @@ + + 4.0.0 + + com.example + example-nested-parent-scope-empty + 1.0.1 + pom + + + + + org.example + example-scope-empty + 2.0.0 + compile + + + + + diff --git a/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-runtime/1.0.0/example-nested-scope-runtime-1.0.0.pom b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-runtime/1.0.0/example-nested-scope-runtime-1.0.0.pom new file mode 100644 index 000000000000..8e48dcd812c3 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-runtime/1.0.0/example-nested-scope-runtime-1.0.0.pom @@ -0,0 +1,41 @@ + + + + 4.0.0 + + + com.example + example-nested-parent-scope-runtime + 1.0.1 + ./parent + + + org.example + example-nested-scope-runtime + 1.0.0 + + jar + Example pom with example-scope-runtime 2.0.0 + + + + + org.example + example-api-runtime + 3.0.0 + test + + + + + + + org.example + example-scope-runtime + 2.0.0 + runtime + + + \ No newline at end of file diff --git a/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-runtime/1.0.0/parent/pom.xml b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-runtime/1.0.0/parent/pom.xml new file mode 100644 index 000000000000..f59df7902390 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-nested-scope-runtime/1.0.0/parent/pom.xml @@ -0,0 +1,21 @@ + + 4.0.0 + + com.example + example-nested-parent-scope-runtime + 1.0.1 + pom + + + + + org.example + example-scope-runtime + 2.0.0 + test + + + + + diff --git a/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-compile/2.0.0/example-scope-compile-2.0.0.pom b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-compile/2.0.0/example-scope-compile-2.0.0.pom new file mode 100644 index 000000000000..1d5bc02ff3b9 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-compile/2.0.0/example-scope-compile-2.0.0.pom @@ -0,0 +1,23 @@ + + + + 4.0.0 + + org.example + example-scope-compile + 2.0.0 + + jar + Example pom with example-api-compile 3.0.0 + + + + org.example + example-api-compile + 3.0.0 + compile + + + \ No newline at end of file diff --git a/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-empty/2.0.0/example-scope-empty-2.0.0.pom b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-empty/2.0.0/example-scope-empty-2.0.0.pom new file mode 100644 index 000000000000..82ce31008fc3 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-empty/2.0.0/example-scope-empty-2.0.0.pom @@ -0,0 +1,22 @@ + + + + 4.0.0 + + org.example + example-scope-empty + 2.0.0 + + jar + Example pom with example-api-empty 3.0.0 + + + + org.example + example-api-empty + 3.0.0 + + + \ No newline at end of file diff --git a/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-runtime/2.0.0/example-scope-runtime-2.0.0.pom b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-runtime/2.0.0/example-scope-runtime-2.0.0.pom new file mode 100644 index 000000000000..90bf973def80 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/repository/org/example/example-scope-runtime/2.0.0/example-scope-runtime-2.0.0.pom @@ -0,0 +1,23 @@ + + + + 4.0.0 + + org.example + example-scope-runtime + 2.0.0 + + jar + Example pom with example-api-runtime 3.0.0 + + + + org.example + example-api-runtime + 3.0.0 + runtime + + + \ No newline at end of file diff --git a/pkg/dependency/parser/java/pom/testdata/transitive-parents/base/pom.xml b/pkg/dependency/parser/java/pom/testdata/transitive-parents/base/pom.xml index 5662c0c9e8de..ae1d6ced6e3f 100644 --- a/pkg/dependency/parser/java/pom/testdata/transitive-parents/base/pom.xml +++ b/pkg/dependency/parser/java/pom/testdata/transitive-parents/base/pom.xml @@ -13,7 +13,7 @@ com.example parent 3.0.0 - ../parent + ../ diff --git a/pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/parent/pom.xml b/pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/parent/pom.xml new file mode 100644 index 000000000000..dec887e6eecc --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/parent/pom.xml @@ -0,0 +1,34 @@ + + + + 4.0.0 + + + org.example + example-top-parent + 5.0.0 + ../top-parent/pom.xml + + + org.example + example-parent + 4.0.0 + pom + + + 4.0.1 + + + + org.example + example-api2 + + + org.example + example-api3 + 4.0.3 + compile + + + \ No newline at end of file diff --git a/pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/pom.xml b/pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/pom.xml new file mode 100644 index 000000000000..651fbba9f885 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/pom.xml @@ -0,0 +1,45 @@ + + 4.0.0 + + + org.example + example-parent + 4.0.0 + ./parent/pom.xml + + + com.example + child-depManagement-in-parent + 1.0.0 + + + + 1.0.1 + 1.0.2 + 1.0.3 + + + + + + org.example + example-api + ${api.version} + runtime + + + org.example + example-api2 + ${api2.version} + runtime + + + org.example + example-api3 + ${api3.version} + runtime + + + + diff --git a/pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/top-parent/pom.xml b/pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/top-parent/pom.xml new file mode 100644 index 000000000000..84d8954a4bf9 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/use-child-dep-management-in-parent/top-parent/pom.xml @@ -0,0 +1,34 @@ + + + + 4.0.0 + + org.example + example-top-parent + 5.0.0 + pom + + + 5.0.1 + + + + + org.example + example-api + ${api.version} + compile + + + org.example + example-api2 + + + org.example + example-api3 + 5.0.3 + compile + + + \ No newline at end of file From 922949a43ea21b10858962330ef8d8297ff40b0a Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Thu, 17 Oct 2024 12:12:44 +0400 Subject: [PATCH 040/108] test: use forked images (#7755) Signed-off-by: knqyf263 --- integration/testimages.ini | 4 ++-- pkg/attestation/attestation_test.go | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/integration/testimages.ini b/integration/testimages.ini index 1a7b8ef1a0c0..778fbc9b8e63 100644 --- a/integration/testimages.ini +++ b/integration/testimages.ini @@ -1,3 +1,3 @@ # Configuration file for both shell scripts and Go programs -TEST_IMAGES=ghcr.io/aquasecurity/trivy-test-images -TEST_VM_IMAGES=ghcr.io/aquasecurity/trivy-test-vm-images +TEST_IMAGES=ghcr.io/knqyf263/trivy-test-images +TEST_VM_IMAGES=ghcr.io/knqyf263/trivy-test-vm-images diff --git a/pkg/attestation/attestation_test.go b/pkg/attestation/attestation_test.go index de75b5676692..0233ba30cf78 100644 --- a/pkg/attestation/attestation_test.go +++ b/pkg/attestation/attestation_test.go @@ -9,7 +9,6 @@ import ( slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common" "github.com/stretchr/testify/require" - "github.com/aquasecurity/trivy/internal/testutil" "github.com/aquasecurity/trivy/pkg/attestation" ) @@ -28,7 +27,7 @@ func TestStatement_UnmarshalJSON(t *testing.T) { PredicateType: "cosign.sigstore.dev/attestation/v1", Subject: []in_toto.Subject{ { - Name: testutil.ImageName("", "", ""), + Name: "ghcr.io/aquasecurity/trivy-test-images", // Defined in the attestations.json file Digest: slsa.DigestSet{ "sha256": "72c42ed48c3a2db31b7dafe17d275b634664a708d901ec9fd57b1529280f01fb", }, From a7baa93b00b8636aa097e64cdb8eed97dbd68511 Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Thu, 17 Oct 2024 11:35:09 +0200 Subject: [PATCH 041/108] feat(parser): ignore white space in pom.xml files (#7747) Signed-off-by: Samuel Gaist --- pkg/dependency/parser/java/pom/artifact.go | 2 +- pkg/dependency/parser/java/pom/parse_test.go | 64 +++++++++++++++++++ .../java/pom/testdata/with-spaces/pom.xml | 31 +++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 pkg/dependency/parser/java/pom/testdata/with-spaces/pom.xml diff --git a/pkg/dependency/parser/java/pom/artifact.go b/pkg/dependency/parser/java/pom/artifact.go index 73ebe6b21407..8fc20b236b51 100644 --- a/pkg/dependency/parser/java/pom/artifact.go +++ b/pkg/dependency/parser/java/pom/artifact.go @@ -163,7 +163,7 @@ func evaluateVariable(s string, props map[string]string, seenProps []string) str } s = strings.ReplaceAll(s, m[0], newValue) } - return s + return strings.TrimSpace(s) } func printLoopedPropertiesStack(env string, usedProps []string) { diff --git a/pkg/dependency/parser/java/pom/parse_test.go b/pkg/dependency/parser/java/pom/parse_test.go index 7b77c0a7ef5e..8d0bf002d6dc 100644 --- a/pkg/dependency/parser/java/pom/parse_test.go +++ b/pkg/dependency/parser/java/pom/parse_test.go @@ -1974,6 +1974,70 @@ func TestPom_Parse(t *testing.T) { }, }, }, + //[INFO] com.example:root-pom-with-spaces:jar:1.0.0 + //[INFO] \- org.example:example-nested:jar:3.3.3:compile + //[INFO] \- org.example:example-dependency:jar:1.2.4:compile + //[INFO] \- org.example:example-api:jar:2.0.0:compile + { + name: "space at the start and/or end of the text nodes", + inputFile: filepath.Join("testdata", "with-spaces", "pom.xml"), + local: true, + want: []ftypes.Package{ + { + ID: "com.example:root-pom-with-spaces:1.0.0", + Name: "com.example:root-pom-with-spaces", + Version: "1.0.0", + Relationship: ftypes.RelationshipRoot, + }, + { + ID: "org.example:example-nested:3.3.3", + Name: "org.example:example-nested", + Version: "3.3.3", + Relationship: ftypes.RelationshipDirect, + Locations: ftypes.Locations{ + { + StartLine: 24, + EndLine: 28, + }, + }, + }, + { + ID: "org.example:example-api:2.0.0", + Name: "org.example:example-api", + Version: "2.0.0", + Licenses: []string{"The Apache Software License, Version 2.0"}, + Relationship: ftypes.RelationshipIndirect, + }, + // dependency version is taken from `com.example:root-pom-with-spaces` from dependencyManagement + // not from `com.example:example-nested` from `com.example:example-nested` + { + ID: "org.example:example-dependency:1.2.4", + Name: "org.example:example-dependency", + Version: "1.2.4", + Relationship: ftypes.RelationshipIndirect, + }, + }, + wantDeps: []ftypes.Dependency{ + { + ID: "com.example:root-pom-with-spaces:1.0.0", + DependsOn: []string{ + "org.example:example-nested:3.3.3", + }, + }, + { + ID: "org.example:example-dependency:1.2.4", + DependsOn: []string{ + "org.example:example-api:2.0.0", + }, + }, + { + ID: "org.example:example-nested:3.3.3", + DependsOn: []string{ + "org.example:example-dependency:1.2.4", + }, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/dependency/parser/java/pom/testdata/with-spaces/pom.xml b/pkg/dependency/parser/java/pom/testdata/with-spaces/pom.xml new file mode 100644 index 000000000000..7a0a1013aa31 --- /dev/null +++ b/pkg/dependency/parser/java/pom/testdata/with-spaces/pom.xml @@ -0,0 +1,31 @@ + + 4.0.0 + + com.example + root-pom-with-spaces + 1.0.0 + + + 1.2.4 + + + + + + org.example + example-dependency + ${dependency.version} + + + + + + + org.example + example-nested + 3.3.3 + + + + From cd44bb48f8ff3cca20bb9b4c6ab3dce1907fc528 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Fri, 18 Oct 2024 03:50:12 +0600 Subject: [PATCH 042/108] refactor(misconf): simplify k8s scanner (#7717) Signed-off-by: nikpivkin --- pkg/iac/scanners/helm/scanner.go | 2 +- pkg/iac/scanners/kubernetes/parser/parser.go | 57 +- pkg/iac/scanners/kubernetes/scanner.go | 112 +--- pkg/iac/scanners/kubernetes/scanner_test.go | 666 ++++--------------- 4 files changed, 132 insertions(+), 705 deletions(-) diff --git a/pkg/iac/scanners/helm/scanner.go b/pkg/iac/scanners/helm/scanner.go index bfa63a5d2c00..daf8f3108628 100644 --- a/pkg/iac/scanners/helm/scanner.go +++ b/pkg/iac/scanners/helm/scanner.go @@ -130,7 +130,7 @@ func (s *Scanner) getScanResults(path string, ctx context.Context, target fs.FS) file := file s.logger.Debug("Processing rendered chart file", log.FilePath(file.TemplateFilePath)) - manifests, err := kparser.New().Parse(strings.NewReader(file.ManifestContent), file.TemplateFilePath) + manifests, err := kparser.Parse(ctx, strings.NewReader(file.ManifestContent), file.TemplateFilePath) if err != nil { return nil, fmt.Errorf("unmarshal yaml: %w", err) } diff --git a/pkg/iac/scanners/kubernetes/parser/parser.go b/pkg/iac/scanners/kubernetes/parser/parser.go index f3ca7d613562..57d723801c98 100644 --- a/pkg/iac/scanners/kubernetes/parser/parser.go +++ b/pkg/iac/scanners/kubernetes/parser/parser.go @@ -5,69 +5,14 @@ import ( "encoding/json" "fmt" "io" - "io/fs" - "path/filepath" "regexp" "strings" "gopkg.in/yaml.v3" kyaml "sigs.k8s.io/yaml" - - "github.com/aquasecurity/trivy/pkg/log" ) -type Parser struct { - logger *log.Logger -} - -// New creates a new K8s parser -func New() *Parser { - return &Parser{ - logger: log.WithPrefix("k8s parser"), - } -} - -func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) (map[string][]any, error) { - files := make(map[string][]any) - if err := fs.WalkDir(target, filepath.ToSlash(path), func(path string, entry fs.DirEntry, err error) error { - select { - case <-ctx.Done(): - return ctx.Err() - default: - } - if err != nil { - return err - } - if entry.IsDir() { - return nil - } - - parsed, err := p.ParseFile(ctx, target, path) - if err != nil { - p.logger.Error("Parse error", log.FilePath(path), log.Err(err)) - return nil - } - - files[path] = parsed - return nil - }); err != nil { - return nil, err - } - return files, nil -} - -// ParseFile parses Kubernetes manifest from the provided filesystem path. -func (p *Parser) ParseFile(_ context.Context, fsys fs.FS, path string) ([]any, error) { - f, err := fsys.Open(filepath.ToSlash(path)) - if err != nil { - return nil, err - } - defer func() { _ = f.Close() }() - return p.Parse(f, path) -} - -func (p *Parser) Parse(r io.Reader, path string) ([]any, error) { - +func Parse(_ context.Context, r io.Reader, path string) ([]any, error) { contents, err := io.ReadAll(r) if err != nil { return nil, err diff --git a/pkg/iac/scanners/kubernetes/scanner.go b/pkg/iac/scanners/kubernetes/scanner.go index dfd248cc8b1d..f79d59aaaf12 100644 --- a/pkg/iac/scanners/kubernetes/scanner.go +++ b/pkg/iac/scanners/kubernetes/scanner.go @@ -3,119 +3,17 @@ package kubernetes import ( "context" "io" - "io/fs" - "path/filepath" - "sort" - "sync" - "github.com/liamg/memoryfs" - - "github.com/aquasecurity/trivy/pkg/iac/framework" - "github.com/aquasecurity/trivy/pkg/iac/rego" - "github.com/aquasecurity/trivy/pkg/iac/scan" - "github.com/aquasecurity/trivy/pkg/iac/scanners" + "github.com/aquasecurity/trivy/pkg/iac/scanners/generic" "github.com/aquasecurity/trivy/pkg/iac/scanners/kubernetes/parser" "github.com/aquasecurity/trivy/pkg/iac/scanners/options" "github.com/aquasecurity/trivy/pkg/iac/types" - "github.com/aquasecurity/trivy/pkg/log" ) -var _ scanners.FSScanner = (*Scanner)(nil) -var _ options.ConfigurableScanner = (*Scanner)(nil) - -type Scanner struct { - mu sync.Mutex - logger *log.Logger - options []options.ScannerOption - regoScanner *rego.Scanner - parser *parser.Parser -} - -func (s *Scanner) SetIncludeDeprecatedChecks(bool) {} -func (s *Scanner) SetRegoOnly(bool) {} -func (s *Scanner) SetFrameworks(frameworks []framework.Framework) {} - -func NewScanner(opts ...options.ScannerOption) *Scanner { - s := &Scanner{ - options: opts, - logger: log.WithPrefix("k8s scanner"), - parser: parser.New(), - } - for _, opt := range opts { - opt(s) - } - return s -} - -func (s *Scanner) Name() string { - return "Kubernetes" -} - -func (s *Scanner) initRegoScanner(srcFS fs.FS) (*rego.Scanner, error) { - s.mu.Lock() - defer s.mu.Unlock() - if s.regoScanner != nil { - return s.regoScanner, nil - } - regoScanner := rego.NewScanner(types.SourceKubernetes, s.options...) - if err := regoScanner.LoadPolicies(srcFS); err != nil { - return nil, err - } - s.regoScanner = regoScanner - return regoScanner, nil +func NewScanner(opts ...options.ScannerOption) *generic.GenericScanner { + return generic.NewScanner("Kubernetes", types.SourceKubernetes, generic.ParseFunc(parse), opts...) } -func (s *Scanner) ScanReader(ctx context.Context, filename string, reader io.Reader) (scan.Results, error) { - memfs := memoryfs.New() - if err := memfs.MkdirAll(filepath.Base(filename), 0o700); err != nil { - return nil, err - } - data, err := io.ReadAll(reader) - if err != nil { - return nil, err - } - if err := memfs.WriteFile(filename, data, 0o644); err != nil { - return nil, err - } - return s.ScanFS(ctx, memfs, ".") -} - -func (s *Scanner) ScanFS(ctx context.Context, target fs.FS, dir string) (scan.Results, error) { - - k8sFilesets, err := s.parser.ParseFS(ctx, target, dir) - if err != nil { - return nil, err - } - - if len(k8sFilesets) == 0 { - return nil, nil - } - - var inputs []rego.Input - for path, k8sFiles := range k8sFilesets { - for _, content := range k8sFiles { - inputs = append(inputs, rego.Input{ - Path: path, - FS: target, - Contents: content, - }) - } - } - - regoScanner, err := s.initRegoScanner(target) - if err != nil { - return nil, err - } - - s.logger.Debug("Scanning files", log.Int("count", len(inputs))) - results, err := regoScanner.ScanInput(ctx, inputs...) - if err != nil { - return nil, err - } - results.SetSourceAndFilesystem("", target, false) - - sort.Slice(results, func(i, j int) bool { - return results[i].Rule().AVDID < results[j].Rule().AVDID - }) - return results, nil +func parse(ctx context.Context, r io.Reader, path string) (any, error) { + return parser.Parse(ctx, r, path) } diff --git a/pkg/iac/scanners/kubernetes/scanner_test.go b/pkg/iac/scanners/kubernetes/scanner_test.go index ef1ac38560f4..c9186a7f9c40 100644 --- a/pkg/iac/scanners/kubernetes/scanner_test.go +++ b/pkg/iac/scanners/kubernetes/scanner_test.go @@ -1,24 +1,23 @@ -package kubernetes +package kubernetes_test import ( "context" - "os" + "io/fs" "strings" "testing" + "testing/fstest" + "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/aquasecurity/trivy/internal/testutil" - "github.com/aquasecurity/trivy/pkg/iac/framework" "github.com/aquasecurity/trivy/pkg/iac/rego" "github.com/aquasecurity/trivy/pkg/iac/scan" + "github.com/aquasecurity/trivy/pkg/iac/scanners/kubernetes" ) -func Test_BasicScan_YAML(t *testing.T) { - - fs := testutil.CreateFS(t, map[string]string{ - "/code/example.yaml": ` +func Test_ScanYAML(t *testing.T) { + file := ` apiVersion: v1 kind: Pod metadata: @@ -28,148 +27,48 @@ spec: - command: ["sh", "-c", "echo 'Hello' && sleep 1h"] image: busybox name: hello -`, - "/rules/rule.rego": ` +` + fsys := buildFS(map[string]string{ + "code/example.yaml": file, + "checks/rule.rego": `# METADATA +# title: test check +# custom: +# id: KSV011 +# avd_id: AVD-KSV-0011 +# severity: LOW +# input: +# selector: +# - type: kubernetes package builtin.kubernetes.KSV011 import data.lib.kubernetes -import data.lib.utils - -default failLimitsCPU = false - -__rego_metadata__ := { - "id": "KSV011", - "avd_id": "AVD-KSV-0011", - "title": "CPU not limited", - "short_code": "limit-cpu", - "version": "v1.0.0", - "severity": "LOW", - "type": "Kubernetes Security Check", - "description": "Enforcing CPU limits prevents DoS via resource exhaustion.", - "recommended_actions": "Set a limit value under 'containers[].resources.limits.cpu'.", - "url": "https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-resource-requests-and-limits", -} - -__rego_input__ := { - "combine": false, - "selector": [{"type": "kubernetes"}], -} - -# getLimitsCPUContainers returns all containers which have set resources.limits.cpu -getLimitsCPUContainers[container] { - allContainers := kubernetes.containers[_] - utils.has_key(allContainers.resources.limits, "cpu") - container := allContainers.name -} - -# getNoLimitsCPUContainers returns all containers which have not set -# resources.limits.cpu -getNoLimitsCPUContainers[container] { - container := kubernetes.containers[_].name - not getLimitsCPUContainers[container] -} - -# failLimitsCPU is true if containers[].resources.limits.cpu is not set -# for ANY container -failLimitsCPU { - count(getNoLimitsCPUContainers) > 0 -} deny[res] { - failLimitsCPU - - msg := kubernetes.format(sprintf("Container '%s' of %s '%s' should set 'resources.limits.cpu'", [getNoLimitsCPUContainers[_], kubernetes.kind, kubernetes.name])) - - res := { - "msg": msg, - "id": __rego_metadata__.id, - "title": __rego_metadata__.title, - "severity": __rego_metadata__.severity, - "type": __rego_metadata__.type, - "startline": 6, - "endline": 10, - } + container := kubernetes.containers[_] + res := result.new("fail", container) } `, }) - scanner := NewScanner( - rego.WithPolicyDirs("rules"), + scanner := kubernetes.NewScanner( + rego.WithPolicyFilesystem(fsys), + rego.WithPolicyDirs("checks"), rego.WithEmbeddedLibraries(true), ) - results, err := scanner.ScanFS(context.TODO(), fs, "code") + results, err := scanner.ScanFS(context.TODO(), fsys, "code") require.NoError(t, err) - require.Len(t, results.GetFailed(), 1) - - assert.Equal(t, scan.Rule{ - AVDID: "AVD-KSV-0011", - Aliases: []string{"KSV011"}, - ShortCode: "limit-cpu", - Summary: "CPU not limited", - Explanation: "Enforcing CPU limits prevents DoS via resource exhaustion.", - Impact: "", - Resolution: "Set a limit value under 'containers[].resources.limits.cpu'.", - Provider: "kubernetes", - Service: "general", - Links: []string{"https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-resource-requests-and-limits"}, - Severity: "LOW", - Terraform: &scan.EngineMetadata{}, - CloudFormation: &scan.EngineMetadata{}, - CustomChecks: scan.CustomChecks{Terraform: (*scan.TerraformCustomCheck)(nil)}, - RegoPackage: "data.builtin.kubernetes.KSV011", - Frameworks: map[framework.Framework][]string{ - framework.Default: {}, - }, - }, results.GetFailed()[0].Rule()) + failed := results.GetFailed() + require.Len(t, failed, 1) - failure := results.GetFailed()[0] - actualCode, err := failure.GetCode() - require.NoError(t, err) - for i := range actualCode.Lines { - actualCode.Lines[i].Highlighted = "" - } - assert.Equal(t, []scan.Line{ - { - Number: 6, - Content: "spec: ", - IsCause: true, - FirstCause: true, - Annotation: "", - }, - { - Number: 7, - Content: " containers: ", - IsCause: true, - Annotation: "", - }, - { - Number: 8, - Content: " - command: [\"sh\", \"-c\", \"echo 'Hello' && sleep 1h\"]", - IsCause: true, - Annotation: "", - }, - { - Number: 9, - Content: " image: busybox", - IsCause: true, - Annotation: "", - }, - { - Number: 10, - Content: " name: hello", - IsCause: true, - LastCause: true, - Annotation: "", - }, - }, actualCode.Lines) + assert.Equal(t, "AVD-KSV-0011", failed[0].Rule().AVDID) + assertLines(t, file, failed) } -func Test_BasicScan_JSON(t *testing.T) { +func Test_ScanJSON(t *testing.T) { - fs := testutil.CreateFS(t, map[string]string{ - "/code/example.json": ` + file := ` { "apiVersion": "v1", "kind": "Pod", @@ -190,165 +89,58 @@ func Test_BasicScan_JSON(t *testing.T) { ] } } -`, - "/rules/rule.rego": ` +` + + fsys := buildFS(map[string]string{ + "code/example.json": file, + "checks/rule.rego": `# METADATA +# title: test check +# custom: +# id: KSV011 +# avd_id: AVD-KSV-0011 +# severity: LOW +# input: +# selector: +# - type: kubernetes package builtin.kubernetes.KSV011 import data.lib.kubernetes -import data.lib.utils - -default failLimitsCPU = false - -__rego_metadata__ := { - "id": "KSV011", - "avd_id": "AVD-KSV-0011", - "title": "CPU not limited", - "short_code": "limit-cpu", - "version": "v1.0.0", - "severity": "LOW", - "type": "Kubernetes Security Check", - "description": "Enforcing CPU limits prevents DoS via resource exhaustion.", - "recommended_actions": "Set a limit value under 'containers[].resources.limits.cpu'.", - "url": "https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-resource-requests-and-limits", -} - -__rego_input__ := { - "combine": false, - "selector": [{"type": "kubernetes"}], -} - -# getLimitsCPUContainers returns all containers which have set resources.limits.cpu -getLimitsCPUContainers[container] { - allContainers := kubernetes.containers[_] - utils.has_key(allContainers.resources.limits, "cpu") - container := allContainers.name -} - -# getNoLimitsCPUContainers returns all containers which have not set -# resources.limits.cpu -getNoLimitsCPUContainers[container] { - container := kubernetes.containers[_].name - not getLimitsCPUContainers[container] -} - -# failLimitsCPU is true if containers[].resources.limits.cpu is not set -# for ANY container -failLimitsCPU { - count(getNoLimitsCPUContainers) > 0 -} deny[res] { - failLimitsCPU - - msg := kubernetes.format(sprintf("Container '%s' of %s '%s' should set 'resources.limits.cpu'", [getNoLimitsCPUContainers[_], kubernetes.kind, kubernetes.name])) - - res := { - "msg": msg, - "id": __rego_metadata__.id, - "title": __rego_metadata__.title, - "severity": __rego_metadata__.severity, - "type": __rego_metadata__.type, - "startline": 6, - "endline": 10, - } + container := kubernetes.containers[_] + res := result.new("fail", container) } `, }) - scanner := NewScanner( - rego.WithPolicyDirs("rules"), + scanner := kubernetes.NewScanner( + rego.WithPolicyFilesystem(fsys), + rego.WithPolicyDirs("checks"), rego.WithEmbeddedLibraries(true), ) - results, err := scanner.ScanFS(context.TODO(), fs, "code") + results, err := scanner.ScanFS(context.TODO(), fsys, "code") require.NoError(t, err) require.Len(t, results.GetFailed(), 1) - assert.Equal(t, scan.Rule{ - AVDID: "AVD-KSV-0011", - Aliases: []string{"KSV011"}, - ShortCode: "limit-cpu", - Summary: "CPU not limited", - Explanation: "Enforcing CPU limits prevents DoS via resource exhaustion.", - Impact: "", - Resolution: "Set a limit value under 'containers[].resources.limits.cpu'.", - Provider: "kubernetes", - Service: "general", - Links: []string{"https://cloud.google.com/blog/products/containers-kubernetes/kubernetes-best-practices-resource-requests-and-limits"}, - Severity: "LOW", - Terraform: &scan.EngineMetadata{}, - CloudFormation: &scan.EngineMetadata{}, - CustomChecks: scan.CustomChecks{Terraform: (*scan.TerraformCustomCheck)(nil)}, - RegoPackage: "data.builtin.kubernetes.KSV011", - Frameworks: map[framework.Framework][]string{ - framework.Default: {}, - }, - }, results.GetFailed()[0].Rule()) + failed := results.GetFailed() + require.Len(t, failed, 1) - failure := results.GetFailed()[0] - actualCode, err := failure.GetCode() - require.NoError(t, err) - for i := range actualCode.Lines { - actualCode.Lines[i].Highlighted = "" - } - assert.Equal(t, []scan.Line{ - { - Number: 6, - Content: ` "name": "hello-cpu-limit"`, - IsCause: true, - FirstCause: true, - Annotation: "", - }, - { - Number: 7, - Content: ` },`, - IsCause: true, - Annotation: "", - }, - { - Number: 8, - Content: ` "spec": {`, - IsCause: true, - Annotation: "", - }, - { - Number: 9, - Content: ` "containers": [`, - IsCause: true, - Annotation: "", - }, - { - Number: 10, - Content: ` {`, - IsCause: true, - LastCause: true, - Annotation: "", - }, - }, actualCode.Lines) + assert.Equal(t, "AVD-KSV-0011", failed[0].Rule().AVDID) + assertLines(t, file, failed) } -func Test_FileScan(t *testing.T) { +func Test_YamlWithSeparator(t *testing.T) { - results, err := NewScanner(rego.WithEmbeddedPolicies(true), rego.WithEmbeddedLibraries(true), rego.WithEmbeddedLibraries(true)).ScanReader(context.TODO(), "k8s.yaml", strings.NewReader(` -apiVersion: v1 -kind: Pod -metadata: - name: hello-cpu-limit -spec: - containers: - - command: ["sh", "-c", "echo 'Hello' && sleep 1h"] - image: busybox - name: hello -`)) - require.NoError(t, err) - - assert.NotEmpty(t, results.GetFailed()) -} - -func Test_FileScan_WithSeparator(t *testing.T) { + fsys := buildFS(map[string]string{ + "check.rego": `package defsec - results, err := NewScanner(rego.WithEmbeddedPolicies(true), rego.WithEmbeddedLibraries(true)).ScanReader(context.TODO(), "k8s.yaml", strings.NewReader(` +deny[res] { + input.kind == "Pod" + res := result.new("fail", input) +}`, + "k8s.yaml": ` --- --- apiVersion: v1 @@ -360,13 +152,21 @@ spec: - command: ["sh", "-c", "echo 'Hello' && sleep 1h"] image: busybox name: hello -`)) +`, + }) + + scanner := kubernetes.NewScanner( + rego.WithPolicyFilesystem(fsys), + rego.WithPolicyDirs("."), + rego.WithEmbeddedLibraries(true), + ) + results, err := scanner.ScanFS(context.TODO(), fsys, ".") require.NoError(t, err) assert.NotEmpty(t, results.GetFailed()) } -func Test_FileScan_MultiManifests(t *testing.T) { +func Test_YamlMultiDocument(t *testing.T) { file := ` --- apiVersion: v1 @@ -389,306 +189,71 @@ spec: image: busybox name: hello2 ` - - results, err := NewScanner( - rego.WithEmbeddedPolicies(true), - rego.WithEmbeddedLibraries(true), - rego.WithEmbeddedLibraries(true)).ScanReader(context.TODO(), "k8s.yaml", strings.NewReader(file)) - require.NoError(t, err) - - assert.Greater(t, len(results.GetFailed()), 1) - fileLines := strings.Split(file, "\n") - for _, failure := range results.GetFailed() { - actualCode, err := failure.GetCode() - require.NoError(t, err) - assert.NotEmpty(t, actualCode.Lines) - for _, line := range actualCode.Lines { - assert.Greater(t, len(fileLines), line.Number) - assert.Equal(t, line.Content, fileLines[line.Number-1]) - } - } -} - -func Test_FileScanWithPolicyReader(t *testing.T) { - - results, err := NewScanner(rego.WithPolicyReader(strings.NewReader(`package defsec - -deny[msg] { - msg = "fail" -} -`))).ScanReader(context.TODO(), "k8s.yaml", strings.NewReader(` -apiVersion: v1 -kind: Pod -metadata: - name: hello-cpu-limit -spec: - containers: - - command: ["sh", "-c", "echo 'Hello' && sleep 1h"] - image: busybox - name: hello -`)) - require.NoError(t, err) - - assert.Len(t, results.GetFailed(), 1) -} - -func Test_FileScanJSON(t *testing.T) { - - results, err := NewScanner(rego.WithPolicyReader(strings.NewReader(`package defsec - -deny[msg] { - input.kind == "Pod" - msg = "fail" -} -`))).ScanReader(context.TODO(), "k8s.json", strings.NewReader(` -{ - "kind": "Pod", - "apiVersion": "v1", - "metadata": { - "name": "mongo", - "labels": { - "name": "mongo", - "role": "mongo" - } - }, - "spec": { - "volumes": [ - { - "name": "mongo-disk", - "gcePersistentDisk": { - "pdName": "mongo-disk", - "fsType": "ext4" - } - } - ], - "containers": [ - { - "name": "mongo", - "image": "mongo:latest", - "ports": [ - { - "name": "mongo", - "containerPort": 27017 - } - ], - "volumeMounts": [ - { - "name": "mongo-disk", - "mountPath": "/data/db" - } - ] - } - ] - } -} -`)) - require.NoError(t, err) - - assert.Len(t, results.GetFailed(), 1) -} - -func Test_FileScanWithMetadata(t *testing.T) { - - results, err := NewScanner( - rego.WithTrace(os.Stdout), - rego.WithPolicyReader(strings.NewReader(`package defsec - -deny[msg] { - input.kind == "Pod" - msg := { - "msg": "fail", - "startline": 2, - "endline": 2, - "filepath": "chartname/template/serviceAccount.yaml" - } -} -`))).ScanReader( - context.TODO(), - "k8s.yaml", - strings.NewReader(` -apiVersion: v1 -kind: Pod -metadata: - name: hello-cpu-limit -spec: - containers: - - command: ["sh", "-c", "echo 'Hello' && sleep 1h"] - image: busybox - name: hello -`)) - require.NoError(t, err) - - assert.NotEmpty(t, results.GetFailed()) - - firstResult := results.GetFailed()[0] - assert.Equal(t, 2, firstResult.Metadata().Range().GetStartLine()) - assert.Equal(t, 2, firstResult.Metadata().Range().GetEndLine()) - assert.Equal(t, "chartname/template/serviceAccount.yaml", firstResult.Metadata().Range().GetFilename()) -} - -func Test_FileScanExampleWithResultFunction(t *testing.T) { - - results, err := NewScanner( - rego.WithEmbeddedPolicies(true), rego.WithEmbeddedLibraries(true), - rego.WithPolicyReader(strings.NewReader(`package defsec - -import data.lib.kubernetes - -default checkCapsDropAll = false - -__rego_metadata__ := { -"id": "KSV003", -"avd_id": "AVD-KSV-0003", -"title": "Default capabilities not dropped", -"short_code": "drop-default-capabilities", -"version": "v1.0.0", -"severity": "LOW", -"type": "Kubernetes Security Check", -"description": "The container should drop all default capabilities and add only those that are needed for its execution.", -"recommended_actions": "Add 'ALL' to containers[].securityContext.capabilities.drop.", -"url": "https://kubesec.io/basics/containers-securitycontext-capabilities-drop-index-all/", -} - -__rego_input__ := { -"combine": false, -"selector": [{"type": "kubernetes"}], -} - -# Get all containers which include 'ALL' in security.capabilities.drop -getCapsDropAllContainers[container] { -allContainers := kubernetes.containers[_] -lower(allContainers.securityContext.capabilities.drop[_]) == "all" -container := allContainers.name -} - -# Get all containers which don't include 'ALL' in security.capabilities.drop -getCapsNoDropAllContainers[container] { -container := kubernetes.containers[_] -not getCapsDropAllContainers[container.name] -} + fsys := buildFS(map[string]string{ + "check.rego": `package defsec deny[res] { -output := getCapsNoDropAllContainers[_] - -msg := kubernetes.format(sprintf("Container '%s' of %s '%s' should add 'ALL' to 'securityContext.capabilities.drop'", [output.name, kubernetes.kind, kubernetes.name])) + input.kind == "Pod" + res := result.new("fail", input) +}`, + "k8s.yaml": file, + }) -res := result.new(msg, output) -} + scanner := kubernetes.NewScanner( + rego.WithPolicyFilesystem(fsys), + rego.WithPolicyDirs("."), + rego.WithEmbeddedLibraries(true), + ) -`))).ScanReader( - context.TODO(), - "k8s.yaml", - strings.NewReader(` -apiVersion: v1 -kind: Pod -metadata: - name: hello-cpu-limit -spec: - containers: - - command: ["sh", "-c", "echo 'Hello' && sleep 1h"] - image: busybox - name: hello - securityContext: - capabilities: - drop: - - nothing -`)) + results, err := scanner.ScanFS(context.TODO(), fsys, ".") require.NoError(t, err) - require.NotEmpty(t, results.GetFailed()) - - firstResult := results.GetFailed()[0] - assert.Equal(t, 8, firstResult.Metadata().Range().GetStartLine()) - assert.Equal(t, 14, firstResult.Metadata().Range().GetEndLine()) - assert.Equal(t, "k8s.yaml", firstResult.Metadata().Range().GetFilename()) + assertLines(t, file, results) } -func Test_checkPolicyIsApplicable(t *testing.T) { - srcFS := testutil.CreateFS(t, map[string]string{ - "policies/pod_policy.rego": `# METADATA -# title: "Process can elevate its own privileges" -# description: "A program inside the container can elevate its own privileges and run as root, which might give the program control over the container and node." +func Test_CheckWithSubtype(t *testing.T) { + fsys := buildFS(map[string]string{ + "checks/pod_policy.rego": `# METADATA +# title: test check # scope: package # schemas: # - input: schema["kubernetes"] -# related_resources: -# - https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted # custom: # id: KSV001 -# avd_id: AVD-KSV-0999 +# avd_id: AVD-KSV-0001 # severity: MEDIUM -# short_code: no-self-privesc -# recommended_action: "Set 'set containers[].securityContext.allowPrivilegeEscalation' to 'false'." # input: # selector: # - type: kubernetes # subtypes: # - kind: Pod -package builtin.kubernetes.KSV999 +package builtin.kubernetes.KSV001 import data.lib.kubernetes -import data.lib.utils - -default checkAllowPrivilegeEscalation = false - -# getNoPrivilegeEscalationContainers returns the names of all containers which have -# securityContext.allowPrivilegeEscalation set to false. -getNoPrivilegeEscalationContainers[container] { - allContainers := kubernetes.containers[_] - allContainers.securityContext.allowPrivilegeEscalation == false - container := allContainers.name -} - -# getPrivilegeEscalationContainers returns the names of all containers which have -# securityContext.allowPrivilegeEscalation set to true or not set. -getPrivilegeEscalationContainers[container] { - containerName := kubernetes.containers[_].name - not getNoPrivilegeEscalationContainers[containerName] - container := kubernetes.containers[_] -} deny[res] { - output := getPrivilegeEscalationContainers[_] - msg := kubernetes.format(sprintf("Container '%s' of %s '%s' should set 'securityContext.allowPrivilegeEscalation' to false", [output.name, kubernetes.kind, kubernetes.name])) - res := result.new(msg, output) + res := result.new("fail", input) } - `, - "policies/namespace_policy.rego": `# METADATA -# title: "The default namespace should not be used" -# description: "ensure that default namespace should not be used" + "checks/namespace_policy.rego": `# METADATA +# title: test check 2 # scope: package # schemas: # - input: schema["kubernetes"] -# related_resources: -# - https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ # custom: -# id: KSV110 -# avd_id: AVD-KSV-0888 +# id: KSV002 +# avd_id: AVD-KSV-0002 # severity: LOW -# short_code: default-namespace-should-not-be-used -# recommended_action: "Ensure that namespaces are created to allow for appropriate segregation of Kubernetes resources and that all new resources are created in a specific namespace." # input: # selector: # - type: kubernetes # subtypes: # - kind: Namespace -package builtin.kubernetes.KSV888 - -import data.lib.kubernetes - -default defaultNamespaceInUse = false - -defaultNamespaceInUse { - kubernetes.namespace == "default" -} +package builtin.kubernetes.KSV002 deny[res] { - defaultNamespaceInUse - msg := sprintf("%s '%s' should not be set with 'default' namespace", [kubernetes.kind, kubernetes.name]) - res := result.new(msg, input.metadata.namespace) + res := result.new("fail", input) } - `, "test/KSV001/pod.yaml": `apiVersion: v1 kind: Pod @@ -706,18 +271,37 @@ spec: `, }) - scanner := NewScanner( - // rego.WithEmbeddedPolicies(true), rego.WithEmbeddedLibraries(true), + scanner := kubernetes.NewScanner( rego.WithEmbeddedLibraries(true), - rego.WithPolicyDirs("policies/"), - rego.WithPolicyFilesystem(srcFS), + rego.WithPolicyDirs("checks"), + rego.WithPolicyFilesystem(fsys), ) - results, err := scanner.ScanFS(context.TODO(), srcFS, "test/KSV001") + results, err := scanner.ScanFS(context.TODO(), fsys, "test/KSV001") require.NoError(t, err) require.NoError(t, err) require.Len(t, results.GetFailed(), 1) - failure := results.GetFailed()[0].Rule() - assert.Equal(t, "Process can elevate its own privileges", failure.Summary) + failure := results.GetFailed()[0] + + assert.Equal(t, "AVD-KSV-0001", failure.Rule().AVDID) +} + +func assertLines(t *testing.T, content string, results scan.Results) { + lines := strings.Split(content, "\n") + for _, res := range results { + actualCode, err := res.GetCode() + require.NoError(t, err) + assert.NotEmpty(t, actualCode.Lines) + for _, line := range actualCode.Lines { + assert.Greater(t, len(lines), line.Number) + assert.Equal(t, line.Content, lines[line.Number-1]) + } + } +} + +func buildFS(files map[string]string) fs.FS { + return fstest.MapFS(lo.MapValues(files, func(val string, _ string) *fstest.MapFile { + return &fstest.MapFile{Data: []byte(val)} + })) } From 35fd018ae7ad86823f114f0ac2f1376726aee444 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Fri, 18 Oct 2024 10:45:24 +0600 Subject: [PATCH 043/108] fix(misconf): fix for Azure Storage Account network acls adaptation (#7602) Signed-off-by: nikpivkin --- pkg/iac/adapters/arm/storage/adapt.go | 24 ++++++++++----------- pkg/iac/adapters/arm/storage/adapt_test.go | 25 ++++++++++++++++++---- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/pkg/iac/adapters/arm/storage/adapt.go b/pkg/iac/adapters/arm/storage/adapt.go index 186ab5beda9d..26a0d10c8616 100644 --- a/pkg/iac/adapters/arm/storage/adapt.go +++ b/pkg/iac/adapters/arm/storage/adapt.go @@ -18,20 +18,18 @@ func adaptAccounts(deployment azure.Deployment) []storage.Account { var accounts []storage.Account for _, resource := range deployment.GetResourcesByType("Microsoft.Storage/storageAccounts") { - var networkRules []storage.NetworkRule - for _, acl := range resource.Properties.GetMapValue("networkAcls").AsList() { + acl := resource.Properties.GetMapValue("networkAcls") - var bypasses []types.StringValue - bypassProp := acl.GetMapValue("bypass") - for _, bypass := range strings.Split(bypassProp.AsString(), ",") { - bypasses = append(bypasses, types.String(bypass, bypassProp.GetMetadata())) - } + var bypasses []types.StringValue + bypassProp := acl.GetMapValue("bypass") + for _, bypass := range strings.Split(bypassProp.AsString(), ",") { + bypasses = append(bypasses, types.String(strings.TrimSpace(bypass), bypassProp.GetMetadata())) + } - networkRules = append(networkRules, storage.NetworkRule{ - Metadata: acl.GetMetadata(), - Bypass: bypasses, - AllowByDefault: types.Bool(acl.GetMapValue("defaultAction").EqualTo("Allow"), acl.GetMetadata()), - }) + networkRule := storage.NetworkRule{ + Metadata: acl.GetMetadata(), + Bypass: bypasses, + AllowByDefault: types.Bool(acl.GetMapValue("defaultAction").EqualTo("Allow"), acl.GetMetadata()), } var queues []storage.Queue @@ -52,7 +50,7 @@ func adaptAccounts(deployment azure.Deployment) []storage.Account { account := storage.Account{ Metadata: resource.Metadata, - NetworkRules: networkRules, + NetworkRules: []storage.NetworkRule{networkRule}, EnforceHTTPS: resource.Properties.GetMapValue("supportsHttpsTrafficOnly").AsBoolValue(false, resource.Properties.GetMetadata()), Containers: containers, QueueProperties: storage.QueueProperties{ diff --git a/pkg/iac/adapters/arm/storage/adapt_test.go b/pkg/iac/adapters/arm/storage/adapt_test.go index a6965ca1560c..372986f23529 100644 --- a/pkg/iac/adapters/arm/storage/adapt_test.go +++ b/pkg/iac/adapters/arm/storage/adapt_test.go @@ -6,6 +6,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/aquasecurity/trivy/internal/testutil" + "github.com/aquasecurity/trivy/pkg/iac/providers/azure/storage" "github.com/aquasecurity/trivy/pkg/iac/scanners/azure" "github.com/aquasecurity/trivy/pkg/iac/types" ) @@ -43,6 +45,10 @@ func Test_AdaptStorage(t *testing.T) { "minimumTlsVersion": azure.NewValue("TLS1_2", types.NewTestMetadata()), "supportsHttpsTrafficOnly": azure.NewValue(true, types.NewTestMetadata()), "publicNetworkAccess": azure.NewValue("Disabled", types.NewTestMetadata()), + "networkAcls": azure.NewValue(map[string]azure.Value{ + "bypass": azure.NewValue("Logging, Metrics", types.NewTestMetadata()), + "defaultAction": azure.NewValue("Allow", types.NewTestMetadata()), + }, types.NewTestMetadata()), }, types.NewTestMetadata()), }, }, @@ -52,9 +58,20 @@ func Test_AdaptStorage(t *testing.T) { require.Len(t, output.Accounts, 1) - account := output.Accounts[0] - assert.Equal(t, "TLS1_2", account.MinimumTLSVersion.Value()) - assert.True(t, account.EnforceHTTPS.Value()) - assert.False(t, account.PublicNetworkAccess.Value()) + expected := storage.Storage{ + Accounts: []storage.Account{{ + MinimumTLSVersion: types.StringTest("TLS1_2"), + EnforceHTTPS: types.BoolTest(true), + PublicNetworkAccess: types.BoolTest(false), + NetworkRules: []storage.NetworkRule{{ + Bypass: []types.StringValue{ + types.StringTest("Logging"), + types.StringTest("Metrics"), + }, + AllowByDefault: types.BoolTest(true), + }}, + }}, + } + testutil.AssertDefsecEqual(t, expected, output) } From c225883649f58128a99fa2c1cef327d0e57940be Mon Sep 17 00:00:00 2001 From: Johannes Feichtner <343448+Churro@users.noreply.github.com> Date: Fri, 18 Oct 2024 09:10:46 +0200 Subject: [PATCH 044/108] feat(cyclonedx): add file checksums to `CycloneDX` reports (#7507) Co-authored-by: knqyf263 --- .../testdata/conda-cyclonedx.json.golden | 12 + ...fluentd-multiple-lockfiles.cdx.json.golden | 330 ++++++++++++++++++ pkg/commands/artifact/run.go | 4 +- 3 files changed, 344 insertions(+), 2 deletions(-) diff --git a/integration/testdata/conda-cyclonedx.json.golden b/integration/testdata/conda-cyclonedx.json.golden index 7f3a352fcce7..df8f575e9053 100644 --- a/integration/testdata/conda-cyclonedx.json.golden +++ b/integration/testdata/conda-cyclonedx.json.golden @@ -34,6 +34,12 @@ "type": "library", "name": "openssl", "version": "1.1.1q", + "hashes": [ + { + "alg": "SHA-1", + "content": "237db0da53131e4548cb1181337fa0f420299e1f" + } + ], "licenses": [ { "license": { @@ -58,6 +64,12 @@ "type": "library", "name": "pip", "version": "22.2.2", + "hashes": [ + { + "alg": "SHA-1", + "content": "a6a2db7668f1ad541d704369fc66c96a4415aa24" + } + ], "licenses": [ { "license": { diff --git a/integration/testdata/fluentd-multiple-lockfiles.cdx.json.golden b/integration/testdata/fluentd-multiple-lockfiles.cdx.json.golden index 65fc78e6c66f..9f23585a01da 100644 --- a/integration/testdata/fluentd-multiple-lockfiles.cdx.json.golden +++ b/integration/testdata/fluentd-multiple-lockfiles.cdx.json.golden @@ -6169,6 +6169,12 @@ "type": "library", "name": "activesupport", "version": "6.0.2.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "a2cd09dcbaf8ca1951fb8e3f2ebdfe6728ab44f7" + } + ], "licenses": [ { "license": { @@ -6201,6 +6207,12 @@ "type": "library", "name": "addressable", "version": "2.7.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "b4596fdeffcb1c89b24623b6f775a6b054a8323f" + } + ], "licenses": [ { "license": { @@ -6233,6 +6245,12 @@ "type": "library", "name": "concurrent-ruby", "version": "1.1.6", + "hashes": [ + { + "alg": "SHA-1", + "content": "c96749b0390ad63300b13dca6fd83e5508facf18" + } + ], "licenses": [ { "license": { @@ -6265,6 +6283,12 @@ "type": "library", "name": "cool.io", "version": "1.6.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "706a2490b54301e8ae8f2ca8f9f56b279b96ac7b" + } + ], "purl": "pkg:gem/cool.io@1.6.0", "properties": [ { @@ -6290,6 +6314,12 @@ "type": "library", "name": "dig_rb", "version": "1.0.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "04a4a555fe3a7e253098e870cf8a6c8746828829" + } + ], "licenses": [ { "license": { @@ -6322,6 +6352,12 @@ "type": "library", "name": "domain_name", "version": "0.5.20190701", + "hashes": [ + { + "alg": "SHA-1", + "content": "e45a352deedbf1d48c2563caa583d0864d6ac62b" + } + ], "licenses": [ { "license": { @@ -6364,6 +6400,12 @@ "type": "library", "name": "elasticsearch-api", "version": "7.5.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "aac794d1d845525dc57d73d8bd5bda4b7f593ea4" + } + ], "licenses": [ { "license": { @@ -6396,6 +6438,12 @@ "type": "library", "name": "elasticsearch-transport", "version": "7.5.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "29ab0a306cfc109b82ac19c37f288956a4d6d1d9" + } + ], "licenses": [ { "license": { @@ -6428,6 +6476,12 @@ "type": "library", "name": "elasticsearch", "version": "7.5.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "f3996e145e83f80d27ed48f8d2dca84f02c696c3" + } + ], "licenses": [ { "license": { @@ -6460,6 +6514,12 @@ "type": "library", "name": "excon", "version": "0.72.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "8b5c81a189d2748ae488dff8a7b4876493b86f76" + } + ], "licenses": [ { "license": { @@ -6492,6 +6552,12 @@ "type": "library", "name": "faraday", "version": "0.17.3", + "hashes": [ + { + "alg": "SHA-1", + "content": "b8c741fbdc2d729a59e2e855037421040673ca45" + } + ], "licenses": [ { "license": { @@ -6524,6 +6590,12 @@ "type": "library", "name": "ffi-compiler", "version": "1.0.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "b9ffee214ef79e695c14c8703566f7c13be4c2ba" + } + ], "licenses": [ { "license": { @@ -6556,6 +6628,12 @@ "type": "library", "name": "ffi", "version": "1.12.2", + "hashes": [ + { + "alg": "SHA-1", + "content": "e6345da46b7a923b2248bc76d074362e7491376b" + } + ], "licenses": [ { "license": { @@ -6588,6 +6666,12 @@ "type": "library", "name": "fluent-plugin-concat", "version": "2.4.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "b6a0da88821e50d052cb244b57562f00abe79888" + } + ], "licenses": [ { "license": { @@ -6620,6 +6704,12 @@ "type": "library", "name": "fluent-plugin-detect-exceptions", "version": "0.0.13", + "hashes": [ + { + "alg": "SHA-1", + "content": "d1a7b50f7723ead908453f463e24424735be0a56" + } + ], "licenses": [ { "license": { @@ -6652,6 +6742,12 @@ "type": "library", "name": "fluent-plugin-elasticsearch", "version": "3.8.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "d60372f3af2757abd0a4ff68484e9328b2cbe386" + } + ], "licenses": [ { "license": { @@ -6684,6 +6780,12 @@ "type": "library", "name": "fluent-plugin-kubernetes_metadata_filter", "version": "2.4.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "eeb3ce046c69c9c83ed1d4bc949058ef6a124f96" + } + ], "licenses": [ { "license": { @@ -6716,6 +6818,12 @@ "type": "library", "name": "fluent-plugin-multi-format-parser", "version": "1.0.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "9aa245fc07627474621e29f04507a377dfae09d3" + } + ], "licenses": [ { "license": { @@ -6748,6 +6856,12 @@ "type": "library", "name": "fluent-plugin-prometheus", "version": "1.7.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "597a311791f0d05968c558e8015c8bed864137e2" + } + ], "licenses": [ { "license": { @@ -6780,6 +6894,12 @@ "type": "library", "name": "fluent-plugin-systemd", "version": "1.0.2", + "hashes": [ + { + "alg": "SHA-1", + "content": "115901208913bc5031597a20fae33c50c6de6500" + } + ], "licenses": [ { "license": { @@ -6812,6 +6932,12 @@ "type": "library", "name": "fluentd", "version": "1.8.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "5f31ca316e345410e5a5b70b5fafed8a51fd5092" + } + ], "licenses": [ { "license": { @@ -6844,6 +6970,12 @@ "type": "library", "name": "http-accept", "version": "1.7.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "21dedf2ba79a24f86528c2dfc32d17dd9324d9fd" + } + ], "purl": "pkg:gem/http-accept@1.7.0", "properties": [ { @@ -6869,6 +7001,12 @@ "type": "library", "name": "http-cookie", "version": "1.0.3", + "hashes": [ + { + "alg": "SHA-1", + "content": "e6f5b8e237e694b3729797cca134525822769964" + } + ], "licenses": [ { "license": { @@ -6901,6 +7039,12 @@ "type": "library", "name": "http-form_data", "version": "2.2.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "53c844c1f954a9c43b78b8e57f18c0ec965beb1f" + } + ], "licenses": [ { "license": { @@ -6933,6 +7077,12 @@ "type": "library", "name": "http-parser", "version": "1.2.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "7c6889d98755a1fe8859d850892412a0e001ba9a" + } + ], "licenses": [ { "license": { @@ -6965,6 +7115,12 @@ "type": "library", "name": "http", "version": "4.3.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "79032e0328aa1d3ee184a38c50cd93d5bce8998b" + } + ], "licenses": [ { "license": { @@ -6997,6 +7153,12 @@ "type": "library", "name": "http_parser.rb", "version": "0.6.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "0ed80e936eaf7151f540186333c3df436afd46c6" + } + ], "licenses": [ { "license": { @@ -7029,6 +7191,12 @@ "type": "library", "name": "i18n", "version": "1.8.2", + "hashes": [ + { + "alg": "SHA-1", + "content": "d13bccc2521cef33fc4303888b24f327a7369877" + } + ], "licenses": [ { "license": { @@ -7061,6 +7229,12 @@ "type": "library", "name": "kubeclient", "version": "4.6.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "31916cb42ac6b30c68a5422414946d0328be94d4" + } + ], "licenses": [ { "license": { @@ -7093,6 +7267,12 @@ "type": "library", "name": "lru_redux", "version": "1.1.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "c8c5874f406a8fefc655fee48b75dfa276a5b8fa" + } + ], "licenses": [ { "license": { @@ -7125,6 +7305,12 @@ "type": "library", "name": "mime-types-data", "version": "3.2019.1009", + "hashes": [ + { + "alg": "SHA-1", + "content": "8a80ce9bf4961df0184d25699612d29293a05aee" + } + ], "licenses": [ { "license": { @@ -7157,6 +7343,12 @@ "type": "library", "name": "mime-types", "version": "3.3.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "b70aa1555acff548ee282c76ddd562e831483187" + } + ], "licenses": [ { "license": { @@ -7189,6 +7381,12 @@ "type": "library", "name": "minitest", "version": "5.14.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "8fcf25c201eacdf1a0e4db78efcb37ad590f33c0" + } + ], "licenses": [ { "license": { @@ -7221,6 +7419,12 @@ "type": "library", "name": "msgpack", "version": "1.3.3", + "hashes": [ + { + "alg": "SHA-1", + "content": "bda4ff6f3cd395534ba441ccacc4640f9dc43942" + } + ], "licenses": [ { "license": { @@ -7253,6 +7457,12 @@ "type": "library", "name": "multi_json", "version": "1.14.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "fead333877a2db2e2aaca87d8cd1f270952cd42e" + } + ], "licenses": [ { "license": { @@ -7285,6 +7495,12 @@ "type": "library", "name": "multipart-post", "version": "2.1.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "927edb51d5b23a49a417fe1503f196896c0e8034" + } + ], "licenses": [ { "license": { @@ -7317,6 +7533,12 @@ "type": "library", "name": "netrc", "version": "0.11.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "98d7bbb894429413feb5c0a3b766a7945f65e3ba" + } + ], "licenses": [ { "license": { @@ -7349,6 +7571,12 @@ "type": "library", "name": "oj", "version": "3.10.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "b348b933b9c2f6a6e952f6a15c7cbd9f1186815f" + } + ], "licenses": [ { "license": { @@ -7381,6 +7609,12 @@ "type": "library", "name": "prometheus-client", "version": "0.9.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "f829d25ee6b39cdda518f7b6f85be6563d7b5990" + } + ], "licenses": [ { "license": { @@ -7413,6 +7647,12 @@ "type": "library", "name": "public_suffix", "version": "4.0.3", + "hashes": [ + { + "alg": "SHA-1", + "content": "ac88907845a8bec2a0df25cf2e3ef61121e47252" + } + ], "licenses": [ { "license": { @@ -7445,6 +7685,12 @@ "type": "library", "name": "quantile", "version": "0.2.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "07a0817cd9df688930b2d8481a647a7ec321b870" + } + ], "licenses": [ { "license": { @@ -7477,6 +7723,12 @@ "type": "library", "name": "rake", "version": "13.0.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "d01a832a472daf914670adda88b44b419a4d2daf" + } + ], "licenses": [ { "license": { @@ -7509,6 +7761,12 @@ "type": "library", "name": "recursive-open-struct", "version": "1.1.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "5a4a02765d82d6786a832384b890f0a2497c2e14" + } + ], "licenses": [ { "license": { @@ -7541,6 +7799,12 @@ "type": "library", "name": "rest-client", "version": "2.1.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "a7e5943a216b16e0867693f20d5d1604cd015486" + } + ], "licenses": [ { "license": { @@ -7573,6 +7837,12 @@ "type": "library", "name": "serverengine", "version": "2.2.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "16e5806b2d513f6b075355c602aed0f960584267" + } + ], "licenses": [ { "license": { @@ -7605,6 +7875,12 @@ "type": "library", "name": "sigdump", "version": "0.2.4", + "hashes": [ + { + "alg": "SHA-1", + "content": "1bae5c6042dc82a4bec6aacb42f75ba71f7cb634" + } + ], "licenses": [ { "license": { @@ -7637,6 +7913,12 @@ "type": "library", "name": "strptime", "version": "0.2.3", + "hashes": [ + { + "alg": "SHA-1", + "content": "17150d9e40754ea1a732796f224b9be78e77b86a" + } + ], "licenses": [ { "license": { @@ -7669,6 +7951,12 @@ "type": "library", "name": "systemd-journal", "version": "1.3.3", + "hashes": [ + { + "alg": "SHA-1", + "content": "4f310622fe58e95897147736c96d3d42174a3363" + } + ], "licenses": [ { "license": { @@ -7701,6 +7989,12 @@ "type": "library", "name": "thread_safe", "version": "0.3.6", + "hashes": [ + { + "alg": "SHA-1", + "content": "546993ac33864e279ae73e918d6da5d4ca083098" + } + ], "licenses": [ { "license": { @@ -7733,6 +8027,12 @@ "type": "library", "name": "tzinfo-data", "version": "1.2019.3", + "hashes": [ + { + "alg": "SHA-1", + "content": "26832d11382943b02433f3ad1df7653b4cfdf3a2" + } + ], "licenses": [ { "license": { @@ -7765,6 +8065,12 @@ "type": "library", "name": "tzinfo", "version": "1.2.6", + "hashes": [ + { + "alg": "SHA-1", + "content": "5b7db490d431d97366729086683e736d2b5fee99" + } + ], "licenses": [ { "license": { @@ -7797,6 +8103,12 @@ "type": "library", "name": "unf", "version": "0.1.4", + "hashes": [ + { + "alg": "SHA-1", + "content": "85ff87b60a6d16ffddf4db5f5f91c0ef76bacd3d" + } + ], "licenses": [ { "license": { @@ -7829,6 +8141,12 @@ "type": "library", "name": "unf_ext", "version": "0.0.7.6", + "hashes": [ + { + "alg": "SHA-1", + "content": "1b5141ee855f16e832534c2e73d81fec0601ebd3" + } + ], "licenses": [ { "license": { @@ -7861,6 +8179,12 @@ "type": "library", "name": "yajl-ruby", "version": "1.4.1", + "hashes": [ + { + "alg": "SHA-1", + "content": "670f3cd2fc601c9b7fde02b1d8c60e90491a7221" + } + ], "licenses": [ { "license": { @@ -7893,6 +8217,12 @@ "type": "library", "name": "zeitwerk", "version": "2.3.0", + "hashes": [ + { + "alg": "SHA-1", + "content": "803894c06d28932016866a26fc2b22c4db942094" + } + ], "licenses": [ { "license": { diff --git a/pkg/commands/artifact/run.go b/pkg/commands/artifact/run.go index 49c7d3b34296..5018434d10c2 100644 --- a/pkg/commands/artifact/run.go +++ b/pkg/commands/artifact/run.go @@ -538,9 +538,9 @@ func (r *runner) initScannerConfig(ctx context.Context, opts flag.Options) (Scan } } - // SPDX needs to calculate digests for package files + // SPDX and CycloneDX need to calculate digests for package files var fileChecksum bool - if opts.Format == types.FormatSPDXJSON || opts.Format == types.FormatSPDX { + if opts.Format == types.FormatSPDXJSON || opts.Format == types.FormatSPDX || opts.Format == types.FormatCycloneDX { fileChecksum = true } From 8d5dbc9fec3569b22ed81a03c40eaf732768718b Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Sat, 19 Oct 2024 06:58:51 +0600 Subject: [PATCH 045/108] fix(misconf): properly expand dynamic blocks (#7612) Signed-off-by: nikpivkin Co-authored-by: Simar --- pkg/iac/scanners/terraform/module_test.go | 2 +- .../scanners/terraform/parser/evaluator.go | 49 +-- .../scanners/terraform/parser/parser_test.go | 400 +++++++++++------- pkg/iac/terraform/attribute.go | 9 +- pkg/iac/terraform/block.go | 163 ++++++- 5 files changed, 409 insertions(+), 214 deletions(-) diff --git a/pkg/iac/scanners/terraform/module_test.go b/pkg/iac/scanners/terraform/module_test.go index d0d289a9d562..ec4291fd59e4 100644 --- a/pkg/iac/scanners/terraform/module_test.go +++ b/pkg/iac/scanners/terraform/module_test.go @@ -567,7 +567,7 @@ resource "something" "else" { for_each = toset(["true"]) content { - ok = each.value + ok = blah.value } } } diff --git a/pkg/iac/scanners/terraform/parser/evaluator.go b/pkg/iac/scanners/terraform/parser/evaluator.go index 809498f35c63..8e2e737b5d9b 100644 --- a/pkg/iac/scanners/terraform/parser/evaluator.go +++ b/pkg/iac/scanners/terraform/parser/evaluator.go @@ -260,36 +260,17 @@ func (e *evaluator) evaluateSteps() { } func (e *evaluator) expandBlocks(blocks terraform.Blocks) terraform.Blocks { - return e.expandDynamicBlocks(e.expandBlockForEaches(e.expandBlockCounts(blocks), false)...) + return e.expandDynamicBlocks(e.expandBlockForEaches(e.expandBlockCounts(blocks))...) } func (e *evaluator) expandDynamicBlocks(blocks ...*terraform.Block) terraform.Blocks { for _, b := range blocks { - e.expandDynamicBlock(b) - } - return blocks -} - -func (e *evaluator) expandDynamicBlock(b *terraform.Block) { - for _, sub := range b.AllBlocks() { - e.expandDynamicBlock(sub) - } - for _, sub := range b.AllBlocks().OfType("dynamic") { - if sub.IsExpanded() { - continue - } - blockName := sub.TypeLabel() - expanded := e.expandBlockForEaches(terraform.Blocks{sub}, true) - for _, ex := range expanded { - if content := ex.GetBlock("content"); content.IsNotNil() { - _ = e.expandDynamicBlocks(content) - b.InjectBlock(content, blockName) - } - } - if len(expanded) > 0 { - sub.MarkExpanded() + if err := b.ExpandBlock(); err != nil { + e.logger.Error(`Failed to expand dynamic block.`, + log.String("block", b.FullName()), log.Err(err)) } } + return blocks } func isBlockSupportsForEachMetaArgument(block *terraform.Block) bool { @@ -297,11 +278,10 @@ func isBlockSupportsForEachMetaArgument(block *terraform.Block) bool { "module", "resource", "data", - "dynamic", }, block.Type()) } -func (e *evaluator) expandBlockForEaches(blocks terraform.Blocks, isDynamic bool) terraform.Blocks { +func (e *evaluator) expandBlockForEaches(blocks terraform.Blocks) terraform.Blocks { var forEachFiltered terraform.Blocks @@ -348,7 +328,7 @@ func (e *evaluator) expandBlockForEaches(blocks terraform.Blocks, isDynamic bool // is the value of the collection. The exception is the use of for-each inside a dynamic block, // because in this case the collection element may not be a primitive value. if (forEachVal.Type().IsCollectionType() || forEachVal.Type().IsTupleType()) && - !forEachVal.Type().IsMapType() && !isDynamic { + !forEachVal.Type().IsMapType() { stringVal, err := convert.Convert(val, cty.String) if err != nil { e.logger.Error( @@ -374,22 +354,7 @@ func (e *evaluator) expandBlockForEaches(blocks terraform.Blocks, isDynamic bool ctx.Set(eachObj, "each") ctx.Set(eachObj, block.TypeLabel()) - - if isDynamic { - if iterAttr := block.GetAttribute("iterator"); iterAttr.IsNotNil() { - refs := iterAttr.AllReferences() - if len(refs) == 1 { - ctx.Set(idx, refs[0].TypeLabel(), "key") - ctx.Set(val, refs[0].TypeLabel(), "value") - } else { - e.logger.Debug("Ignoring iterator attribute in dynamic block, expected one reference", - log.Int("refs", len(refs))) - } - } - } - forEachFiltered = append(forEachFiltered, clone) - clones[idx.AsString()] = clone.Values() }) diff --git a/pkg/iac/scanners/terraform/parser/parser_test.go b/pkg/iac/scanners/terraform/parser/parser_test.go index e3bd817748f6..540338afb57f 100644 --- a/pkg/iac/scanners/terraform/parser/parser_test.go +++ b/pkg/iac/scanners/terraform/parser/parser_test.go @@ -1367,139 +1367,284 @@ func TestCountMetaArgumentInModule(t *testing.T) { } func TestDynamicBlocks(t *testing.T) { - t.Run("arg is list of int", func(t *testing.T) { - modules := parse(t, map[string]string{ - "main.tf": ` -resource "aws_security_group" "sg-webserver" { - vpc_id = "1111" - dynamic "ingress" { + tests := []struct { + name string + src string + expected []any + }{ + { + name: "for-each use tuple of int", + src: `resource "test_resource" "test" { + dynamic "foo" { for_each = [80, 443] content { - from_port = ingress.value - to_port = ingress.value - protocol = "tcp" - cidr_blocks = ["0.0.0.0/0"] + bar = foo.value } } -} -`, - }) - require.Len(t, modules, 1) - - secGroups := modules.GetResourcesByType("aws_security_group") - assert.Len(t, secGroups, 1) - ingressBlocks := secGroups[0].GetBlocks("ingress") - assert.Len(t, ingressBlocks, 2) - - var inboundPorts []int - for _, ingress := range ingressBlocks { - fromPort := ingress.GetAttribute("from_port").AsIntValueOrDefault(-1, ingress).Value() - inboundPorts = append(inboundPorts, fromPort) - } - - assert.True(t, compareSets([]int{80, 443}, inboundPorts)) - }) - - t.Run("empty for-each", func(t *testing.T) { - modules := parse(t, map[string]string{ - "main.tf": ` -resource "aws_lambda_function" "analyzer" { - dynamic "vpc_config" { +}`, + expected: []any{float64(80), float64(443)}, + }, + { + name: "for-each use list of int", + src: `resource "test_resource" "test" { + dynamic "foo" { + for_each = tolist([80, 443]) + content { + bar = foo.value + } + } +}`, + expected: []any{float64(80), float64(443)}, + }, + { + name: "for-each use set of int", + src: `resource "test_resource" "test" { + dynamic "foo" { + for_each = toset([80, 443]) + content { + bar = foo.value + } + } +}`, + expected: []any{float64(80), float64(443)}, + }, + { + name: "for-each use list of bool", + src: `resource "test_resource" "test" { + dynamic "foo" { + for_each = tolist([true]) + content { + bar = foo.value + } + } +}`, + expected: []any{true}, + }, + { + name: "empty for-each", + src: `resource "test_resource" "test" { + dynamic "foo" { for_each = [] content {} } +}`, + expected: []any{}, + }, + { + name: "for-each use tuple of objects", + src: `variable "test_var" { + type = list(object({ enabled = bool })) + default = [{ enabled = true }] } -`, - }) - require.Len(t, modules, 1) - functions := modules.GetResourcesByType("aws_lambda_function") - assert.Len(t, functions, 1) - vpcConfigs := functions[0].GetBlocks("vpc_config") - assert.Empty(t, vpcConfigs) - }) +resource "test_resource" "test" { + dynamic "foo" { + for_each = var.test_var - t.Run("arg is list of bool", func(t *testing.T) { - modules := parse(t, map[string]string{ - "main.tf": ` -resource "aws_lambda_function" "analyzer" { - dynamic "vpc_config" { - for_each = [true] - content {} + content { + bar = foo.value.enabled + } + } +}`, + expected: []any{true}, + }, + { + name: "attribute ref to object key", + src: `variable "some_var" { + type = map( + object({ + tag = string + }) + ) + default = { + ssh = { "tag" = "login" } + http = { "tag" = "proxy" } + https = { "tag" = "proxy" } } } -`, - }) - require.Len(t, modules, 1) - - functions := modules.GetResourcesByType("aws_lambda_function") - assert.Len(t, functions, 1) - vpcConfigs := functions[0].GetBlocks("vpc_config") - assert.Len(t, vpcConfigs, 1) - }) - t.Run("arg is list of objects", func(t *testing.T) { - modules := parse(t, map[string]string{ - "main.tf": `locals { - cluster_network_policy = [{ - enabled = true - }] +resource "test_resource" "test" { + dynamic "foo" { + for_each = { for name, values in var.some_var : name => values } + content { + bar = foo.key + } + } +}`, + expected: []any{"ssh", "http", "https"}, + }, + { + name: "attribute ref to object value", + src: `variable "some_var" { + type = map( + object({ + tag = string + }) + ) + default = { + ssh = { "tag" = "login" } + http = { "tag" = "proxy" } + https = { "tag" = "proxy" } + } } -resource "google_container_cluster" "primary" { - name = "test" +resource "test_resource" "test" { + dynamic "foo" { + for_each = { for name, values in var.some_var : name => values } + content { + bar = foo.value.tag + } + } +}`, + expected: []any{"login", "proxy", "proxy"}, + }, + { + name: "attribute ref to map key", + src: `variable "some_var" { + type = map + default = { + ssh = { "tag" = "login" } + http = { "tag" = "proxy" } + https = { "tag" = "proxy" } + } +} - dynamic "network_policy" { - for_each = local.cluster_network_policy +resource "test_resource" "test" { + dynamic "foo" { + for_each = var.some_var + content { + bar = foo.key + } + } +}`, + expected: []any{"ssh", "http", "https"}, + }, + { + name: "attribute ref to map value", + src: `variable "some_var" { + type = map + default = { + ssh = { "tag" = "login" } + http = { "tag" = "proxy" } + https = { "tag" = "proxy" } + } +} +resource "test_resource" "test" { + dynamic "foo" { + for_each = var.some_var content { - enabled = network_policy.value.enabled + bar = foo.value.tag } } }`, - }) - require.Len(t, modules, 1) + expected: []any{"login", "proxy", "proxy"}, + }, + { + name: "dynamic block with iterator", + src: `resource "test_resource" "test" { + dynamic "foo" { + for_each = ["foo", "bar"] + iterator = some_iterator + content { + bar = some_iterator.value + } + } +}`, + expected: []any{"foo", "bar"}, + }, + { + name: "iterator and parent block with same name", + src: `resource "test_resource" "test" { + dynamic "foo" { + for_each = ["foo", "bar"] + iterator = foo + content { + bar = foo.value + } + } +}`, + expected: []any{"foo", "bar"}, + }, + { + name: "for-each use null value", + src: `resource "test_resource" "test" { + dynamic "foo" { + for_each = null + content { + bar = foo.value + } + } +}`, + expected: []any{}, + }, + { + name: "no for-each attribute", + src: `resource "test_resource" "test" { + dynamic "foo" { + content { + bar = foo.value + } + } +}`, + expected: []any{}, + }, + } - clusters := modules.GetResourcesByType("google_container_cluster") - assert.Len(t, clusters, 1) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + modules := parse(t, map[string]string{ + "main.tf": tt.src, + }) + require.Len(t, modules, 1) - networkPolicies := clusters[0].GetBlocks("network_policy") - assert.Len(t, networkPolicies, 1) + resource := modules.GetResourcesByType("test_resource") + require.Len(t, resource, 1) + blocks := resource[0].GetBlocks("foo") - enabled := networkPolicies[0].GetAttribute("enabled") - assert.True(t, enabled.Value().True()) - }) + var vals []any + for _, attr := range blocks { + vals = append(vals, attr.GetAttribute("bar").GetRawValue()) + } - t.Run("nested dynamic", func(t *testing.T) { - modules := parse(t, map[string]string{ - "main.tf": ` -resource "test_block" "this" { - name = "name" - location = "loc" - dynamic "env" { - for_each = ["1", "2"] - content { - dynamic "value_source" { - for_each = [true, true] - content {} - } + assert.ElementsMatch(t, tt.expected, vals) + }) } +} + +func TestNestedDynamicBlock(t *testing.T) { + modules := parse(t, map[string]string{ + "main.tf": `resource "test_resource" "test" { + dynamic "foo" { + for_each = ["1", "1"] + content { + dynamic "bar" { + for_each = [true, true] + content { + baz = foo.value + qux = bar.value + } + } + } } }`, - }) - require.Len(t, modules, 1) + }) + require.Len(t, modules, 1) - testResources := modules.GetResourcesByType("test_block") - assert.Len(t, testResources, 1) - envs := testResources[0].GetBlocks("env") - assert.Len(t, envs, 2) + testResources := modules.GetResourcesByType("test_resource") + assert.Len(t, testResources, 1) + blocks := testResources[0].GetBlocks("foo") + assert.Len(t, blocks, 2) - var sources []*terraform.Block - for _, env := range envs { - sources = append(sources, env.GetBlocks("value_source")...) + var nested []*terraform.Block + for _, block := range blocks { + nested = append(nested, block.GetBlocks("bar")...) + for _, b := range nested { + assert.Equal(t, "1", b.GetAttribute("baz").GetRawValue()) + assert.Equal(t, true, b.GetAttribute("qux").GetRawValue()) } - assert.Len(t, sources, 4) - }) + } + assert.Len(t, nested, 4) } func parse(t *testing.T, files map[string]string) terraform.Modules { @@ -1513,21 +1658,6 @@ func parse(t *testing.T, files map[string]string) terraform.Modules { return modules } -func compareSets(a, b []int) bool { - m := make(map[int]bool) - for _, el := range a { - m[el] = true - } - - for _, el := range b { - if !m[el] { - return false - } - } - - return true -} - func TestModuleRefersToOutputOfAnotherModule(t *testing.T) { files := map[string]string{ "main.tf": ` @@ -1775,42 +1905,6 @@ variable "foo" {} assert.Equal(t, "bar", blocks[0].GetAttribute("foo").Value().AsString()) } -func TestDynamicWithIterator(t *testing.T) { - fsys := fstest.MapFS{ - "main.tf": &fstest.MapFile{ - Data: []byte(`resource "aws_s3_bucket" "this" { - dynamic versioning { - for_each = [true] - iterator = ver - - content { - enabled = ver.value - } - } -}`), - }, - } - - parser := New( - fsys, "", - OptionStopOnHCLError(true), - OptionWithDownloads(false), - ) - require.NoError(t, parser.ParseFS(context.TODO(), ".")) - - modules, _, err := parser.EvaluateAll(context.TODO()) - require.NoError(t, err) - - assert.Len(t, modules, 1) - - buckets := modules.GetResourcesByType("aws_s3_bucket") - assert.Len(t, buckets, 1) - - attr, _ := buckets[0].GetNestedAttribute("versioning.enabled") - - assert.True(t, attr.Value().True()) -} - func Test_AWSRegionNameDefined(t *testing.T) { fs := testutil.CreateFS(t, map[string]string{ diff --git a/pkg/iac/terraform/attribute.go b/pkg/iac/terraform/attribute.go index 39161a72a379..9c5536357283 100644 --- a/pkg/iac/terraform/attribute.go +++ b/pkg/iac/terraform/attribute.go @@ -729,15 +729,16 @@ func (a *Attribute) IsTrue() bool { if a == nil { return false } - switch a.Value().Type() { + val := a.Value() + switch val.Type() { case cty.Bool: - return a.Value().True() + return val.True() case cty.String: - val := a.Value().AsString() + val := val.AsString() val = strings.Trim(val, "\"") return strings.EqualFold(val, "true") case cty.Number: - val := a.Value().AsBigFloat() + val := val.AsBigFloat() f, _ := val.Float64() return f > 0 } diff --git a/pkg/iac/terraform/block.go b/pkg/iac/terraform/block.go index dbf7352959d0..dd6d8446c124 100644 --- a/pkg/iac/terraform/block.go +++ b/pkg/iac/terraform/block.go @@ -1,6 +1,7 @@ package terraform import ( + "errors" "fmt" "io/fs" "strconv" @@ -137,16 +138,15 @@ func (b *Block) GetRawValue() any { return nil } -func (b *Block) InjectBlock(block *Block, name string) { - block.hclBlock.Labels = []string{} - block.hclBlock.Type = name +func (b *Block) injectBlock(block *Block) { for attrName, attr := range block.Attributes() { - b.context.Root().SetByDot(attr.Value(), fmt.Sprintf("%s.%s.%s", b.reference.String(), name, attrName)) + path := fmt.Sprintf("%s.%s.%s", b.reference.String(), block.hclBlock.Type, attrName) + b.context.Root().SetByDot(attr.Value(), path) } b.childBlocks = append(b.childBlocks, block) } -func (b *Block) MarkExpanded() { +func (b *Block) markExpanded() { b.expanded = true } @@ -154,17 +154,26 @@ func (b *Block) IsExpanded() bool { return b.expanded } -func (b *Block) Clone(index cty.Value) *Block { - var childCtx *context.Context - if b.context != nil { - childCtx = b.context.NewChild() - } else { - childCtx = context.NewContext(&hcl.EvalContext{}, nil) +func (b *Block) inherit(ctx *context.Context, index ...cty.Value) *Block { + return NewBlock(b.copyBlock(), ctx, b.moduleBlock, b.parentBlock, b.moduleSource, b.moduleFS, index...) +} + +func (b *Block) copyBlock() *hcl.Block { + hclBlock := *b.hclBlock + return &hclBlock +} + +func (b *Block) childContext() *context.Context { + if b.context == nil { + return context.NewContext(&hcl.EvalContext{}, nil) } + return b.context.NewChild() +} - cloneHCL := *b.hclBlock +func (b *Block) Clone(index cty.Value) *Block { + childCtx := b.childContext() + clone := b.inherit(childCtx, index) - clone := NewBlock(&cloneHCL, childCtx, b.moduleBlock, b.parentBlock, b.moduleSource, b.moduleFS, index) if len(clone.hclBlock.Labels) > 0 { position := len(clone.hclBlock.Labels) - 1 labels := make([]string, len(clone.hclBlock.Labels)) @@ -188,7 +197,7 @@ func (b *Block) Clone(index cty.Value) *Block { } indexVal, _ := gocty.ToCtyValue(index, cty.Number) clone.context.SetByDot(indexVal, "count.index") - clone.MarkExpanded() + clone.markExpanded() b.cloneIndex++ return clone } @@ -446,6 +455,17 @@ func (b *Block) LocalName() string { return b.reference.String() } +func (b *Block) FullLocalName() string { + if b.parentBlock != nil { + return fmt.Sprintf( + "%s.%s", + b.parentBlock.FullLocalName(), + b.LocalName(), + ) + } + return b.LocalName() +} + func (b *Block) FullName() string { if b.moduleBlock != nil { @@ -576,3 +596,118 @@ func (b *Block) IsNil() bool { func (b *Block) IsNotNil() bool { return !b.IsNil() } + +func (b *Block) ExpandBlock() error { + var ( + expanded []*Block + errs []error + ) + + for _, child := range b.childBlocks { + if child.Type() == "dynamic" { + blocks, err := child.expandDynamic() + if err != nil { + errs = append(errs, err) + continue + } + expanded = append(expanded, blocks...) + } + } + + for _, block := range expanded { + b.injectBlock(block) + } + + return errors.Join(errs...) +} + +func (b *Block) expandDynamic() ([]*Block, error) { + if b.IsExpanded() || b.Type() != "dynamic" { + return nil, nil + } + + realBlockType := b.TypeLabel() + if realBlockType == "" { + return nil, errors.New("dynamic block must have 1 label") + } + + forEachVal, err := b.validateForEach() + if err != nil { + return nil, fmt.Errorf("invalid for-each in %s block: %w", b.FullLocalName(), err) + } + + var ( + expanded []*Block + errs []error + ) + + forEachVal.ForEachElement(func(key, val cty.Value) (stop bool) { + if val.IsNull() { + return + } + + iteratorName, err := b.iteratorName(realBlockType) + if err != nil { + errs = append(errs, err) + return + } + + forEachCtx := b.childContext() + obj := cty.ObjectVal(map[string]cty.Value{ + "key": key, + "value": val, + }) + forEachCtx.Set(obj, iteratorName) + + if content := b.GetBlock("content"); content != nil { + inherited := content.inherit(forEachCtx) + inherited.hclBlock.Labels = []string{} + inherited.hclBlock.Type = realBlockType + if err := inherited.ExpandBlock(); err != nil { + errs = append(errs, err) + return + } + expanded = append(expanded, inherited) + } + return + }) + + if len(expanded) > 0 { + b.markExpanded() + } + + return expanded, errors.Join(errs...) +} + +func (b *Block) validateForEach() (cty.Value, error) { + forEachAttr := b.GetAttribute("for_each") + if forEachAttr == nil { + return cty.NilVal, errors.New("for_each attribute required") + } + + forEachVal := forEachAttr.Value() + + if !forEachVal.CanIterateElements() { + return cty.NilVal, fmt.Errorf("cannot use a %s value in for_each. An iterable collection is required", forEachVal.GoString()) + } + + return forEachVal, nil +} + +func (b *Block) iteratorName(blockType string) (string, error) { + iteratorAttr := b.GetAttribute("iterator") + if iteratorAttr == nil { + return blockType, nil + } + + traversal, diags := hcl.AbsTraversalForExpr(iteratorAttr.hclAttribute.Expr) + if diags.HasErrors() { + return "", diags + } + + if len(traversal) != 1 { + return "", fmt.Errorf("dynamic iterator must be a single variable name") + } + + return traversal.RootName(), nil +} From 57e24aa85382f749df7f673e241caaf3fcbb45cb Mon Sep 17 00:00:00 2001 From: Matthieu MOREL Date: Mon, 21 Oct 2024 11:06:50 +0200 Subject: [PATCH 046/108] fix: enable usestdlibvars linter (#7770) Signed-off-by: Matthieu MOREL --- .golangci.yaml | 1 + integration/registry_test.go | 2 +- pkg/dependency/parser/java/pom/parse.go | 2 +- pkg/iac/adapters/terraform/aws/apigateway/adapt_test.go | 3 ++- pkg/iac/adapters/terraform/aws/apigateway/apiv1_test.go | 5 +++-- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 95c66cc69f73..40bfa36e1a64 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -105,6 +105,7 @@ linters: - typecheck - unconvert - unused + - usestdlibvars run: go: '1.22' diff --git a/integration/registry_test.go b/integration/registry_test.go index aded09adce35..25ddaf5b2814 100644 --- a/integration/registry_test.go +++ b/integration/registry_test.go @@ -334,7 +334,7 @@ func requestRegistryToken(imageRef name.Reference, baseDir string, opt registryO } // Get a registry token - req, err := http.NewRequest("GET", fmt.Sprintf("%s/auth", opt.AuthURL), nil) + req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/auth", opt.AuthURL), nil) if err != nil { return "", err } diff --git a/pkg/dependency/parser/java/pom/parse.go b/pkg/dependency/parser/java/pom/parse.go index 2c8a194ea50b..60a83ba9d81b 100644 --- a/pkg/dependency/parser/java/pom/parse.go +++ b/pkg/dependency/parser/java/pom/parse.go @@ -703,7 +703,7 @@ func (p *Parser) remoteRepoRequest(repo string, paths []string) (*http.Request, paths = append([]string{repoURL.Path}, paths...) repoURL.Path = path.Join(paths...) - req, err := http.NewRequest("GET", repoURL.String(), http.NoBody) + req, err := http.NewRequest(http.MethodGet, repoURL.String(), http.NoBody) if err != nil { return nil, xerrors.Errorf("unable to create HTTP request: %w", err) } diff --git a/pkg/iac/adapters/terraform/aws/apigateway/adapt_test.go b/pkg/iac/adapters/terraform/aws/apigateway/adapt_test.go index 65393ae82f82..8d2b0155fee5 100644 --- a/pkg/iac/adapters/terraform/aws/apigateway/adapt_test.go +++ b/pkg/iac/adapters/terraform/aws/apigateway/adapt_test.go @@ -1,6 +1,7 @@ package apigateway import ( + "net/http" "testing" "github.com/stretchr/testify/assert" @@ -72,7 +73,7 @@ resource "aws_apigatewayv2_domain_name" "example" { { Methods: []v1.Method{ { - HTTPMethod: String("GET"), + HTTPMethod: String(http.MethodGet), AuthorizationType: String("NONE"), APIKeyRequired: Bool(false), }, diff --git a/pkg/iac/adapters/terraform/aws/apigateway/apiv1_test.go b/pkg/iac/adapters/terraform/aws/apigateway/apiv1_test.go index cf69e8d0ee23..9932ac864e7b 100644 --- a/pkg/iac/adapters/terraform/aws/apigateway/apiv1_test.go +++ b/pkg/iac/adapters/terraform/aws/apigateway/apiv1_test.go @@ -1,6 +1,7 @@ package apigateway import ( + "net/http" "testing" "github.com/aquasecurity/trivy/internal/testutil" @@ -35,7 +36,7 @@ resource "aws_api_gateway_method" "example" { `, expected: []v1.Method{ { - HTTPMethod: String("GET"), + HTTPMethod: String(http.MethodGet), AuthorizationType: String("NONE"), APIKeyRequired: Bool(false), }, @@ -63,7 +64,7 @@ resource "aws_api_gateway_method" "example" { `, expected: []v1.Method{ { - HTTPMethod: String("GET"), + HTTPMethod: String(http.MethodGet), AuthorizationType: String("NONE"), APIKeyRequired: Bool(true), }, From f6acdf713991f8ffdbe765178fcb8a9cde433cba Mon Sep 17 00:00:00 2001 From: Pierre Baumard Date: Mon, 21 Oct 2024 11:38:35 +0200 Subject: [PATCH 047/108] fix(license): fix license normalization for Universal Permissive License (#7766) --- pkg/licensing/normalize.go | 2 +- pkg/licensing/normalize_test.go | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/licensing/normalize.go b/pkg/licensing/normalize.go index 4ce294275214..bce81e68903d 100644 --- a/pkg/licensing/normalize.go +++ b/pkg/licensing/normalize.go @@ -626,7 +626,7 @@ func TrimLicenseText(text string) string { } // version number match -var versionRegexpString = "([A-UW-Z)]{2,})( LICENSE)?\\s*[,(-]?\\s*(V|V\\.|VER|VER\\.|VERSION|VERSION-|-)?\\s*([1-9](\\.\\d)*)[)]?" +var versionRegexpString = "([A-UW-Z)])( LICENSE)?\\s*[,(-]?\\s*(V|V\\.|VER|VER\\.|VERSION|VERSION-|-)?\\s*([1-9](\\.\\d)*)[)]?" // case insensitive version match anywhere in string var versionRegexp = regexp.MustCompile("(?i)" + versionRegexpString) diff --git a/pkg/licensing/normalize_test.go b/pkg/licensing/normalize_test.go index 9b47fd923f29..7daf7b57d783 100644 --- a/pkg/licensing/normalize_test.go +++ b/pkg/licensing/normalize_test.go @@ -209,6 +209,13 @@ func TestNormalize(t *testing.T) { normalized: " The unmapped license ", normalizedKey: " The unmapped license ", }, + { + licenses: []string{ + "Universal Permissive License, Version 1.0", + }, + normalized: "UPL-1.0", + normalizedKey: "UPL-1.0", + }, } for _, tt := range tests { t.Run(tt.normalized, func(t *testing.T) { From 010b2138067184114a882d3b5d86e2f556f4355c Mon Sep 17 00:00:00 2001 From: afdesk Date: Tue, 22 Oct 2024 00:25:54 +0600 Subject: [PATCH 048/108] refactor(k8s): scan config files as a folder (#7690) --- pkg/k8s/scanner/io.go | 50 ++++++++++++++++++----- pkg/k8s/scanner/io_test.go | 5 +++ pkg/k8s/scanner/scanner.go | 82 ++++++++++++++++++++++---------------- 3 files changed, 92 insertions(+), 45 deletions(-) diff --git a/pkg/k8s/scanner/io.go b/pkg/k8s/scanner/io.go index 9c32699ddd63..65f102a4b08c 100644 --- a/pkg/k8s/scanner/io.go +++ b/pkg/k8s/scanner/io.go @@ -3,6 +3,7 @@ package scanner import ( "fmt" "os" + "path/filepath" "regexp" "runtime" @@ -13,36 +14,63 @@ import ( "github.com/aquasecurity/trivy/pkg/log" ) -var r = regexp.MustCompile("\\\\|/|:|\\*|\\?|<|>") +var r = regexp.MustCompile("[\\\\/:*?<>]") -func createTempFile(artifact *artifacts.Artifact) (string, error) { +func generateTempFileByArtifact(artifact *artifacts.Artifact, tempDir string) (string, error) { filename := fmt.Sprintf("%s-%s-%s-*.yaml", artifact.Namespace, artifact.Kind, artifact.Name) - if runtime.GOOS == "windows" { // removes characters not permitted in file/directory names on Windows filename = filenameWindowsFriendly(filename) } - file, err := os.CreateTemp("", filename) + file, err := os.CreateTemp(tempDir, filename) if err != nil { - return "", xerrors.Errorf("creating tmp file error: %w", err) + return "", xerrors.Errorf("failed to create temporary file: %w", err) } + shouldRemove := false defer func() { if err := file.Close(); err != nil { - log.Error("Failed to close temp file", log.String("path", file.Name()), log.Err(err)) + log.Error("Failed to close temp file", log.FilePath(file.Name()), log.Err(err)) + } + if shouldRemove { + removeFile(file.Name()) } }() - if err := yaml.NewEncoder(file).Encode(artifact.RawResource); err != nil { - removeFile(filename) - return "", xerrors.Errorf("marshaling resource error: %w", err) + shouldRemove = true + return "", xerrors.Errorf("failed to encode artifact: %w", err) + } + return filepath.Base(file.Name()), nil +} + +// generateTempDir creates a directory with yaml files generated from kubernetes artifacts +// returns a directory name, a map for mapping a temp target file to k8s artifact and error +func generateTempDir(arts []*artifacts.Artifact) (string, map[string]*artifacts.Artifact, error) { + tempDir, err := os.MkdirTemp("", "trivyk8s*") + if err != nil { + return "", nil, xerrors.Errorf("failed to create temp directory: %w", err) + } + + m := make(map[string]*artifacts.Artifact) + for _, artifact := range arts { + filename, err := generateTempFileByArtifact(artifact, tempDir) + if err != nil { + log.Error("Failed to create temp file", log.FilePath(filename), log.Err(err)) + continue + } + m[filename] = artifact } + return tempDir, m, nil +} - return file.Name(), nil +func removeDir(dirname string) { + if err := os.RemoveAll(dirname); err != nil { + log.Error("Failed to remove temp directory", log.FilePath(dirname), log.Err(err)) + } } func removeFile(filename string) { if err := os.Remove(filename); err != nil { - log.Error("Failed to remove temp file", log.String("path", filename), log.Err(err)) + log.Error("Failed to remove temp file", log.FilePath(filename), log.Err(err)) } } diff --git a/pkg/k8s/scanner/io_test.go b/pkg/k8s/scanner/io_test.go index 7587d1bb8282..9e256b39f2b3 100644 --- a/pkg/k8s/scanner/io_test.go +++ b/pkg/k8s/scanner/io_test.go @@ -23,6 +23,11 @@ func Test_FilenameWindowsFriendly(t *testing.T) { fileName: `kube-system-Role-system-controller-bootstrap-signer-2934213283.yaml`, want: `kube-system-Role-system-controller-bootstrap-signer-2934213283.yaml`, }, + { + name: "name with no invalid - slash", + fileName: "-ClusterRoleBinding-system\\basic-user-725844313.yaml", + want: `-ClusterRoleBinding-system_basic-user-725844313.yaml`, + }, } for _, test := range tests { diff --git a/pkg/k8s/scanner/scanner.go b/pkg/k8s/scanner/scanner.go index 67d06b4c54bd..70debfe6a85f 100644 --- a/pkg/k8s/scanner/scanner.go +++ b/pkg/k8s/scanner/scanner.go @@ -82,14 +82,18 @@ func (s *Scanner) Scan(ctx context.Context, artifactsData []*artifacts.Artifact) var resources []report.Resource - type scanResult struct { - vulns []report.Resource - misconfig report.Resource + // scans kubernetes artifacts as a scope of yaml files + if local.ShouldScanMisconfigOrRbac(s.opts.Scanners) { + misconfigs, err := s.scanMisconfigs(ctx, resourceArtifacts) + if err != nil { + return report.Report{}, xerrors.Errorf("scanning misconfigurations error: %w", err) + } + resources = append(resources, misconfigs...) } - onItem := func(ctx context.Context, artifact *artifacts.Artifact) (scanResult, error) { - scanResults := scanResult{} - if s.opts.Scanners.AnyEnabled(types.VulnerabilityScanner, types.SecretScanner) && !s.opts.SkipImages { + // scan images from kubernetes cluster in parallel + if s.opts.Scanners.AnyEnabled(types.VulnerabilityScanner, types.SecretScanner) && !s.opts.SkipImages { + onItem := func(ctx context.Context, artifact *artifacts.Artifact) ([]report.Resource, error) { opts := s.opts opts.Credentials = make([]ftypes.Credential, len(s.opts.Credentials)) copy(opts.Credentials, s.opts.Credentials) @@ -106,33 +110,22 @@ func (s *Scanner) Scan(ctx context.Context, artifactsData []*artifacts.Artifact) } vulns, err := s.scanVulns(ctx, artifact, opts) if err != nil { - return scanResult{}, xerrors.Errorf("scanning vulnerabilities error: %w", err) + return nil, xerrors.Errorf("scanning vulnerabilities error: %w", err) } - scanResults.vulns = vulns + return vulns, nil } - if local.ShouldScanMisconfigOrRbac(s.opts.Scanners) { - misconfig, err := s.scanMisconfigs(ctx, artifact) - if err != nil { - return scanResult{}, xerrors.Errorf("scanning misconfigurations error: %w", err) - } - scanResults.misconfig = misconfig + + onResult := func(result []report.Resource) error { + resources = append(resources, result...) + return nil } - return scanResults, nil - } - onResult := func(result scanResult) error { - resources = append(resources, result.vulns...) - // don't add empty misconfig results to resources slice to avoid an empty resource - if result.misconfig.Results != nil { - resources = append(resources, result.misconfig) + p := parallel.NewPipeline(s.opts.Parallel, !s.opts.Quiet, resourceArtifacts, onItem, onResult) + if err := p.Do(ctx); err != nil { + return report.Report{}, err } - return nil } - p := parallel.NewPipeline(s.opts.Parallel, !s.opts.Quiet, resourceArtifacts, onItem, onResult) - if err := p.Do(ctx); err != nil { - return report.Report{}, err - } if s.opts.Scanners.AnyEnabled(types.VulnerabilityScanner) { k8sResource, err := s.scanK8sVulns(ctx, k8sCoreArtifacts) if err != nil { @@ -173,22 +166,43 @@ func (s *Scanner) scanVulns(ctx context.Context, artifact *artifacts.Artifact, o return resources, nil } -func (s *Scanner) scanMisconfigs(ctx context.Context, artifact *artifacts.Artifact) (report.Resource, error) { - configFile, err := createTempFile(artifact) +func (s *Scanner) scanMisconfigs(ctx context.Context, k8sArtifacts []*artifacts.Artifact) ([]report.Resource, error) { + dir, artifactsByFilename, err := generateTempDir(k8sArtifacts) if err != nil { - return report.Resource{}, xerrors.Errorf("scan error: %w", err) + return nil, xerrors.Errorf("failed to generate temp dir: %w", err) } - s.opts.Target = configFile + s.opts.Target = dir configReport, err := s.runner.ScanFilesystem(ctx, s.opts) - // remove config file after scanning - removeFile(configFile) + // remove config files after scanning + removeDir(dir) + if err != nil { - return report.CreateResource(artifact, configReport, err), err + return nil, xerrors.Errorf("failed to scan filesystem: %w", err) + } + resources := make([]report.Resource, 0, len(k8sArtifacts)) + + for _, res := range configReport.Results { + artifact := artifactsByFilename[res.Target] + + singleReport := types.Report{ + SchemaVersion: configReport.SchemaVersion, + CreatedAt: configReport.CreatedAt, + ArtifactName: res.Target, + ArtifactType: configReport.ArtifactType, + Metadata: configReport.Metadata, + Results: types.Results{res}, + } + + resource, err := s.filter(ctx, singleReport, artifact) + if err != nil { + resource = report.CreateResource(artifact, singleReport, err) + } + resources = append(resources, resource) } - return s.filter(ctx, configReport, artifact) + return resources, nil } func (s *Scanner) filter(ctx context.Context, r types.Report, artifact *artifacts.Artifact) (report.Resource, error) { var err error From 9514148767865baddd73a49245385574927f7a74 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Tue, 22 Oct 2024 02:55:12 +0600 Subject: [PATCH 049/108] feat(misconf): export unresolvable field of IaC types to Rego (#7765) Signed-off-by: nikpivkin --- pkg/iac/rego/convert/slice_test.go | 2 ++ pkg/iac/rego/schemas/cloud.json | 21 +++++++++++++++++++++ pkg/iac/state/state_test.go | 2 ++ pkg/iac/types/metadata.go | 1 + pkg/iac/types/metadata_test.go | 3 +++ 5 files changed, 29 insertions(+) diff --git a/pkg/iac/rego/convert/slice_test.go b/pkg/iac/rego/convert/slice_test.go index e30c200142b2..ff318e2b1a03 100644 --- a/pkg/iac/rego/convert/slice_test.go +++ b/pkg/iac/rego/convert/slice_test.go @@ -38,6 +38,7 @@ func Test_SliceTypesConversion(t *testing.T) { "endline": 123, "sourceprefix": "", "managed": true, + "unresolvable": false, "explicit": false, "fskey": "", "resource": "", @@ -49,6 +50,7 @@ func Test_SliceTypesConversion(t *testing.T) { "endline": 123, "sourceprefix": "", "managed": true, + "unresolvable": false, "explicit": false, "fskey": "", "resource": "", diff --git a/pkg/iac/rego/schemas/cloud.json b/pkg/iac/rego/schemas/cloud.json index 3cc6301429be..bdaad1330898 100644 --- a/pkg/iac/rego/schemas/cloud.json +++ b/pkg/iac/rego/schemas/cloud.json @@ -7884,6 +7884,9 @@ "startline": { "type": "integer" }, + "unresolvable": { + "type": "boolean" + }, "value": { "type": "boolean" } @@ -7916,6 +7919,9 @@ "startline": { "type": "integer" }, + "unresolvable": { + "type": "boolean" + }, "value": { "type": "string" } @@ -7948,6 +7954,9 @@ "startline": { "type": "integer" }, + "unresolvable": { + "type": "boolean" + }, "value": { "type": "integer" } @@ -7980,6 +7989,9 @@ "startline": { "type": "integer" }, + "unresolvable": { + "type": "boolean" + }, "value": { "type": "object" } @@ -8011,6 +8023,9 @@ }, "startline": { "type": "integer" + }, + "unresolvable": { + "type": "boolean" } } }, @@ -8041,6 +8056,9 @@ "startline": { "type": "integer" }, + "unresolvable": { + "type": "boolean" + }, "value": { "type": "string" } @@ -8073,6 +8091,9 @@ "startline": { "type": "integer" }, + "unresolvable": { + "type": "boolean" + }, "value": { "type": "string" } diff --git a/pkg/iac/state/state_test.go b/pkg/iac/state/state_test.go index 60b4b061a2d3..46de9b506d4b 100644 --- a/pkg/iac/state/state_test.go +++ b/pkg/iac/state/state_test.go @@ -44,6 +44,7 @@ func Test_RegoConversion(t *testing.T) { "startline": 2, "endline": 4, "managed": true, + "unresolvable": false, "explicit": false, "fskey": "", }, @@ -55,6 +56,7 @@ func Test_RegoConversion(t *testing.T) { "endline": 3, "value": "my-bucket", "managed": true, + "unresolvable": false, "explicit": false, "fskey": "", }, diff --git a/pkg/iac/types/metadata.go b/pkg/iac/types/metadata.go index 359654bdc43b..c71460441622 100755 --- a/pkg/iac/types/metadata.go +++ b/pkg/iac/types/metadata.go @@ -84,6 +84,7 @@ func (m *Metadata) ToRego() any { "sourceprefix": m.Range().GetSourcePrefix(), "managed": m.isManaged, "explicit": m.isExplicit, + "unresolvable": m.isUnresolvable, "fskey": CreateFSKey(m.Range().GetFS()), "resource": m.Reference(), } diff --git a/pkg/iac/types/metadata_test.go b/pkg/iac/types/metadata_test.go index f447c6d85ca2..f7d46bbf9dbd 100644 --- a/pkg/iac/types/metadata_test.go +++ b/pkg/iac/types/metadata_test.go @@ -8,12 +8,14 @@ import ( func Test_MetadataToRego(t *testing.T) { m1 := NewTestMetadata() + m1.isUnresolvable = true expected := map[string]any{ "endline": 123, "explicit": false, "filepath": "test.test", "fskey": "", "managed": true, + "unresolvable": true, "resource": "", "sourceprefix": "", "startline": 123, @@ -27,6 +29,7 @@ func Test_MetadataToRego(t *testing.T) { "filepath": "test.test", "fskey": "", "managed": true, + "unresolvable": false, "resource": "", "sourceprefix": "", "startline": 123, From f75c0d1f0069d4856cb4826d6049f32c5b9409d9 Mon Sep 17 00:00:00 2001 From: simar7 <1254783+simar7@users.noreply.github.com> Date: Wed, 23 Oct 2024 12:54:29 -0600 Subject: [PATCH 050/108] feat(misconf): Show misconfig ID in output (#7762) --- pkg/report/table/misconfig.go | 12 ++++++------ pkg/report/table/misconfig_test.go | 21 +++++++++++---------- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/pkg/report/table/misconfig.go b/pkg/report/table/misconfig.go index 112d783d0875..dfb724bdf458 100644 --- a/pkg/report/table/misconfig.go +++ b/pkg/report/table/misconfig.go @@ -124,18 +124,18 @@ func (r *misconfigRenderer) renderSummary(misconf types.DetectedMisconfiguration } } - // severity + // ID & severity switch misconf.Severity { case severityCritical: - r.printf("%s: ", misconf.Severity) + r.printf("%s (%s): ", misconf.AVDID, misconf.Severity) case severityHigh: - r.printf("%s: ", misconf.Severity) + r.printf("%s (%s): ", misconf.AVDID, misconf.Severity) case severityMedium: - r.printf("%s: ", misconf.Severity) + r.printf("%s (%s): ", misconf.AVDID, misconf.Severity) case severityLow: - r.printf("%s: ", misconf.Severity) + r.printf("%s (%s): ", misconf.AVDID, misconf.Severity) default: - r.printf("%s: ", misconf.Severity) + r.printf("%s (%s): ", misconf.AVDID, misconf.Severity) } // heading diff --git a/pkg/report/table/misconfig_test.go b/pkg/report/table/misconfig_test.go index a57399f0e253..785b2621b504 100644 --- a/pkg/report/table/misconfig_test.go +++ b/pkg/report/table/misconfig_test.go @@ -27,7 +27,8 @@ func TestMisconfigRenderer(t *testing.T) { MisconfSummary: &types.MisconfSummary{Successes: 0, Failures: 1, Exceptions: 0}, Misconfigurations: []types.DetectedMisconfiguration{ { - ID: "AVD-XYZ-0123", + ID: "some-alias-for-a-check", + AVDID: "AVD-XYZ-0123", Title: "Config file is bad", Description: "Your config file is not good.", Message: "Oh no, a bad config.", @@ -44,7 +45,7 @@ my-file () Tests: 1 (SUCCESSES: 0, FAILURES: 1, EXCEPTIONS: 0) Failures: 1 (LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0) -HIGH: Oh no, a bad config. +AVD-XYZ-0123 (HIGH): Oh no, a bad config. ════════════════════════════════════════ Your config file is not good. @@ -61,7 +62,7 @@ See https://google.com/search?q=bad%20config MisconfSummary: &types.MisconfSummary{Successes: 0, Failures: 1, Exceptions: 0}, Misconfigurations: []types.DetectedMisconfiguration{ { - ID: "AVD-XYZ-0123", + AVDID: "AVD-XYZ-0123", Title: "Config file is bad", Description: "Your config file is not good.", Message: "Oh no, a bad config.", @@ -103,7 +104,7 @@ my-file () Tests: 1 (SUCCESSES: 0, FAILURES: 1, EXCEPTIONS: 0) Failures: 1 (LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0) -HIGH: Oh no, a bad config. +AVD-XYZ-0123 (HIGH): Oh no, a bad config. ════════════════════════════════════════ Your config file is not good. @@ -126,7 +127,7 @@ See https://google.com/search?q=bad%20config MisconfSummary: &types.MisconfSummary{Successes: 1, Failures: 1, Exceptions: 0}, Misconfigurations: []types.DetectedMisconfiguration{ { - ID: "AVD-XYZ-0123", + AVDID: "AVD-XYZ-0123", Title: "Config file is bad", Description: "Your config file is not good.", Message: "Oh no, a bad config.", @@ -157,7 +158,7 @@ See https://google.com/search?q=bad%20config }, }, { - ID: "AVD-XYZ-0456", + AVDID: "AVD-XYZ-0456", Title: "Config file is bad again", Description: "Your config file is still not good.", Message: "Oh no, a bad config AGAIN.", @@ -174,7 +175,7 @@ my-file () Tests: 2 (SUCCESSES: 1, FAILURES: 1, EXCEPTIONS: 0) Failures: 1 (LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0) -FAIL: HIGH: Oh no, a bad config. +FAIL: AVD-XYZ-0123 (HIGH): Oh no, a bad config. ════════════════════════════════════════ Your config file is not good. @@ -188,7 +189,7 @@ See https://google.com/search?q=bad%20config ──────────────────────────────────────── -PASS: MEDIUM: Oh no, a bad config AGAIN. +PASS: AVD-XYZ-0456 (MEDIUM): Oh no, a bad config AGAIN. ════════════════════════════════════════ Your config file is still not good. @@ -213,7 +214,7 @@ See https://google.com/search?q=bad%20config { Type: "Terraform Security Check", ID: "AVD-AWS-0107", - AVDID: "AVS-AWS-0107", + AVDID: "AVD-AWS-0107", Title: "An ingress security group rule allows traffic from /0", Description: "Opening up ports to the public internet is generally to be avoided. You should restrict access to IP addresses or ranges that explicitly require it where possible.", Message: "Security group rule allows ingress from public internet.", @@ -312,7 +313,7 @@ terraform-aws-modules/security-group/aws/main.tf (terraform) Tests: 6 (SUCCESSES: 5, FAILURES: 1, EXCEPTIONS: 0) Failures: 1 (LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 1) -CRITICAL: Security group rule allows ingress from public internet. +AVD-AWS-0107 (CRITICAL): Security group rule allows ingress from public internet. ════════════════════════════════════════ Opening up ports to the public internet is generally to be avoided. You should restrict access to IP addresses or ranges that explicitly require it where possible. From c0d79fa09e645f3a3dbff878e393b8631fb17b64 Mon Sep 17 00:00:00 2001 From: Aaron Goldenthal Date: Thu, 24 Oct 2024 02:24:02 -0500 Subject: [PATCH 051/108] feat(report): update gitlab template to populate operating_system value (#7735) --- contrib/gitlab.tpl | 13 +- integration/client_server_test.go | 11 + integration/testdata/alpine-310.gitlab.golden | 8 +- integration/testdata/npm.gitlab.golden | 214 ++++++++++++++++++ 4 files changed, 239 insertions(+), 7 deletions(-) create mode 100644 integration/testdata/npm.gitlab.golden diff --git a/contrib/gitlab.tpl b/contrib/gitlab.tpl index 744c0c9394cb..7ff8abd8bfec 100644 --- a/contrib/gitlab.tpl +++ b/contrib/gitlab.tpl @@ -24,11 +24,18 @@ "status": "success", "type": "container_scanning" }, + {{- $image := "Unknown" -}} + {{- $os := "Unknown" -}} + {{- range . }} + {{- if eq .Class "os-pkgs" -}} + {{- $target := .Target }} + {{- $image = $target | regexFind "[^\\s]+" }} + {{- $os = $target | splitList "(" | last | trimSuffix ")" }} + {{- end }} + {{- end }} "vulnerabilities": [ {{- $t_first := true }} {{- range . }} - {{- $target := .Target }} - {{- $image := $target | regexFind "[^\\s]+" }} {{- range .Vulnerabilities -}} {{- if $t_first -}} {{- $t_first = false -}} @@ -65,7 +72,7 @@ "version": "{{ .InstalledVersion }}" }, {{- /* TODO: No mapping available - https://github.com/aquasecurity/trivy/issues/332 */}} - "operating_system": "Unknown", + "operating_system": "{{ $os }}", "image": "{{ $image }}" }, "identifiers": [ diff --git a/integration/client_server_test.go b/integration/client_server_test.go index 07a4f086200c..4afed3fdc155 100644 --- a/integration/client_server_test.go +++ b/integration/client_server_test.go @@ -323,6 +323,17 @@ func TestClientServerWithFormat(t *testing.T) { }, golden: "testdata/alpine-310.gitlab.golden", }, + { + name: "scan package-lock.json with gitlab template (Unknown os and image)", + args: csArgs{ + Command: "fs", + Format: "template", + TemplatePath: "@../contrib/gitlab.tpl", + Target: "testdata/fixtures/repo/npm/", + ListAllPackages: true, + }, + golden: "testdata/npm.gitlab.golden", + }, { name: "alpine 3.10 with gitlab-codequality template", args: csArgs{ diff --git a/integration/testdata/alpine-310.gitlab.golden b/integration/testdata/alpine-310.gitlab.golden index ad769b31959e..a0339dcf6bfb 100644 --- a/integration/testdata/alpine-310.gitlab.golden +++ b/integration/testdata/alpine-310.gitlab.golden @@ -37,7 +37,7 @@ }, "version": "1.1.1c-r0" }, - "operating_system": "Unknown", + "operating_system": "alpine 3.10.2", "image": "testdata/fixtures/images/alpine-310.tar.gz" }, "identifiers": [ @@ -104,7 +104,7 @@ }, "version": "1.1.1c-r0" }, - "operating_system": "Unknown", + "operating_system": "alpine 3.10.2", "image": "testdata/fixtures/images/alpine-310.tar.gz" }, "identifiers": [ @@ -191,7 +191,7 @@ }, "version": "1.1.1c-r0" }, - "operating_system": "Unknown", + "operating_system": "alpine 3.10.2", "image": "testdata/fixtures/images/alpine-310.tar.gz" }, "identifiers": [ @@ -258,7 +258,7 @@ }, "version": "1.1.1c-r0" }, - "operating_system": "Unknown", + "operating_system": "alpine 3.10.2", "image": "testdata/fixtures/images/alpine-310.tar.gz" }, "identifiers": [ diff --git a/integration/testdata/npm.gitlab.golden b/integration/testdata/npm.gitlab.golden new file mode 100644 index 000000000000..6c8cc2ce97ba --- /dev/null +++ b/integration/testdata/npm.gitlab.golden @@ -0,0 +1,214 @@ +{ + "version": "15.0.7", + "scan": { + "analyzer": { + "id": "trivy", + "name": "Trivy", + "vendor": { + "name": "Aqua Security" + }, + "version": "dev" + }, + "end_time": "2021-08-25T12:20:30", + "scanner": { + "id": "trivy", + "name": "Trivy", + "url": "https://github.com/aquasecurity/trivy/", + "vendor": { + "name": "Aqua Security" + }, + "version": "dev" + }, + "start_time": "2021-08-25T12:20:30", + "status": "success", + "type": "container_scanning" + }, + "vulnerabilities": [ + { + "id": "CVE-2019-11358", + "name": "jquery: Prototype pollution in object's prototype leading to denial of service, remote code execution, or property injection", + "description": "jQuery before 3.4.0, as used in Drupal, Backdrop CMS, and other products, mishandles jQuery.extend(true, {}, ...) because of Object.prototype pollution. If an unsanitized source object contained an enumerable __proto__ property, it could extend the native Object.prototype.", + "severity": "Medium", + "solution": "Upgrade jquery to 3.4.0", + "location": { + "dependency": { + "package": { + "name": "jquery" + }, + "version": "3.3.9" + }, + "operating_system": "Unknown", + "image": "Unknown" + }, + "identifiers": [ + { + "type": "cve", + "name": "CVE-2019-11358", + "value": "CVE-2019-11358", + "url": "https://avd.aquasec.com/nvd/cve-2019-11358" + } + ], + "links": [{ + "url": "http://lists.opensuse.org/opensuse-security-announce/2019-08/msg00006.html" + },{ + "url": "http://lists.opensuse.org/opensuse-security-announce/2019-08/msg00025.html" + },{ + "url": "http://packetstormsecurity.com/files/152787/dotCMS-5.1.1-Vulnerable-Dependencies.html" + },{ + "url": "http://packetstormsecurity.com/files/153237/RetireJS-CORS-Issue-Script-Execution.html" + },{ + "url": "http://packetstormsecurity.com/files/156743/OctoberCMS-Insecure-Dependencies.html" + },{ + "url": "http://seclists.org/fulldisclosure/2019/May/10" + },{ + "url": "http://seclists.org/fulldisclosure/2019/May/11" + },{ + "url": "http://seclists.org/fulldisclosure/2019/May/13" + },{ + "url": "http://www.openwall.com/lists/oss-security/2019/06/03/2" + },{ + "url": "http://www.securityfocus.com/bid/108023" + },{ + "url": "https://access.redhat.com/errata/RHBA-2019:1570" + },{ + "url": "https://access.redhat.com/errata/RHSA-2019:1456" + },{ + "url": "https://access.redhat.com/errata/RHSA-2019:2587" + },{ + "url": "https://access.redhat.com/errata/RHSA-2019:3023" + },{ + "url": "https://access.redhat.com/errata/RHSA-2019:3024" + },{ + "url": "https://access.redhat.com/security/cve/CVE-2019-11358" + },{ + "url": "https://backdropcms.org/security/backdrop-sa-core-2019-009" + },{ + "url": "https://blog.jquery.com/2019/04/10/jquery-3-4-0-released/" + },{ + "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-11358" + },{ + "url": "https://github.com/DanielRuf/snyk-js-jquery-174006?files=1" + },{ + "url": "https://github.com/advisories/GHSA-6c3j-c64m-qhgq" + },{ + "url": "https://github.com/jquery/jquery/commit/753d591aea698e57d6db58c9f722cd0808619b1b" + },{ + "url": "https://github.com/jquery/jquery/pull/4333" + },{ + "url": "https://github.com/rails/jquery-rails/blob/master/CHANGELOG.md#434" + },{ + "url": "https://hackerone.com/reports/454365" + },{ + "url": "https://kb.pulsesecure.net/articles/Pulse_Security_Advisories/SA44601" + },{ + "url": "https://linux.oracle.com/cve/CVE-2019-11358.html" + },{ + "url": "https://linux.oracle.com/errata/ELSA-2020-4847.html" + },{ + "url": "https://lists.apache.org/thread.html/08720ef215ee7ab3386c05a1a90a7d1c852bf0706f176a7816bf65fc@%3Ccommits.airflow.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/519eb0fd45642dcecd9ff74cb3e71c20a4753f7d82e2f07864b5108f@%3Cdev.drill.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/5928aa293e39d248266472210c50f176cac1535220f2486e6a7fa844@%3Ccommits.airflow.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/6097cdbd6f0a337bedd9bb5cc441b2d525ff002a96531de367e4259f@%3Ccommits.airflow.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/88fb0362fd40e5b605ea8149f63241537b8b6fb5bfa315391fc5cbb7@%3Ccommits.airflow.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/b0656d359c7d40ec9f39c8cc61bca66802ef9a2a12ee199f5b0c1442@%3Cdev.drill.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/b736d0784cf02f5a30fbb4c5902762a15ad6d47e17e2c5a17b7d6205@%3Ccommits.airflow.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/ba79cf1658741e9f146e4c59b50aee56656ea95d841d358d006c18b6@%3Ccommits.roller.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/bcce5a9c532b386c68dab2f6b3ce8b0cc9b950ec551766e76391caa3@%3Ccommits.nifi.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/f9bc3e55f4e28d1dcd1a69aae6d53e609a758e34d2869b4d798e13cc@%3Cissues.drill.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/r2041a75d3fc09dec55adfd95d598b38d22715303f65c997c054844c9@%3Cissues.flink.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/r2baacab6e0acb5a2092eb46ae04fd6c3e8277b4fd79b1ffb7f3254fa@%3Cissues.flink.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/r38f0d1aa3c923c22977fe7376508f030f22e22c1379fbb155bf29766@%3Cdev.syncope.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/r41b5bfe009c845f67d4f68948cc9419ac2d62e287804aafd72892b08@%3Cissues.flink.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/r7aac081cbddb6baa24b75e74abf0929bf309b176755a53e3ed810355@%3Cdev.flink.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/r7d64895cc4dff84d0becfc572b20c0e4bf9bfa7b10c6f5f73e783734@%3Cdev.storm.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/r7e8ebccb7c022e41295f6fdb7b971209b83702339f872ddd8cf8bf73@%3Cissues.flink.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/rac25da84ecdcd36f6de5ad0d255f4e967209bbbebddb285e231da37d@%3Cissues.flink.apache.org%3E" + },{ + "url": "https://lists.apache.org/thread.html/rca37935d661f4689cb4119f1b3b224413b22be161b678e6e6ce0c69b@%3Ccommits.nifi.apache.org%3E" + },{ + "url": "https://lists.debian.org/debian-lts-announce/2019/05/msg00006.html" + },{ + "url": "https://lists.debian.org/debian-lts-announce/2019/05/msg00029.html" + },{ + "url": "https://lists.debian.org/debian-lts-announce/2020/02/msg00024.html" + },{ + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/4UOAZIFCSZ3ENEFOR5IXX6NFAD3HV7FA/" + },{ + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/5IABSKTYZ5JUGL735UKGXL5YPRYOPUYI/" + },{ + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/KYH3OAGR2RTCHRA5NOKX2TES7SNQMWGO/" + },{ + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/QV3PKZC3PQCO3273HAT76PAQZFBEO4KP/" + },{ + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/RLXRX23725JL366CNZGJZ7AQQB7LHQ6F/" + },{ + "url": "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/WZW27UCJ5CYFL4KFFFMYMIBNMIU2ALG5/" + },{ + "url": "https://nvd.nist.gov/vuln/detail/CVE-2019-11358" + },{ + "url": "https://seclists.org/bugtraq/2019/Apr/32" + },{ + "url": "https://seclists.org/bugtraq/2019/Jun/12" + },{ + "url": "https://seclists.org/bugtraq/2019/May/18" + },{ + "url": "https://security.netapp.com/advisory/ntap-20190919-0001/" + },{ + "url": "https://snyk.io/vuln/SNYK-JS-JQUERY-174006" + },{ + "url": "https://www.debian.org/security/2019/dsa-4434" + },{ + "url": "https://www.debian.org/security/2019/dsa-4460" + },{ + "url": "https://www.drupal.org/sa-core-2019-006" + },{ + "url": "https://www.oracle.com//security-alerts/cpujul2021.html" + },{ + "url": "https://www.oracle.com/security-alerts/cpuApr2021.html" + },{ + "url": "https://www.oracle.com/security-alerts/cpuapr2020.html" + },{ + "url": "https://www.oracle.com/security-alerts/cpujan2020.html" + },{ + "url": "https://www.oracle.com/security-alerts/cpujan2021.html" + },{ + "url": "https://www.oracle.com/security-alerts/cpujul2020.html" + },{ + "url": "https://www.oracle.com/security-alerts/cpuoct2020.html" + },{ + "url": "https://www.oracle.com/security-alerts/cpuoct2021.html" + },{ + "url": "https://www.oracle.com/technetwork/security-advisory/cpujul2019-5072835.html" + },{ + "url": "https://www.oracle.com/technetwork/security-advisory/cpuoct2019-5072832.html" + },{ + "url": "https://www.privacy-wise.com/mitigating-cve-2019-11358-in-old-versions-of-jquery/" + },{ + "url": "https://www.synology.com/security/advisory/Synology_SA_19_19" + },{ + "url": "https://www.tenable.com/security/tns-2019-08" + },{ + "url": "https://www.tenable.com/security/tns-2020-02" + } + ] + } + ], + "remediations": [] +} From ad3c09e006e134f3c5b879ffc34ce9895a8c860f Mon Sep 17 00:00:00 2001 From: dean Date: Fri, 25 Oct 2024 07:02:00 +0100 Subject: [PATCH 052/108] feat: add end of life date for Ubuntu 24.10 (#7787) --- pkg/detector/ospkg/ubuntu/ubuntu.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/detector/ospkg/ubuntu/ubuntu.go b/pkg/detector/ospkg/ubuntu/ubuntu.go index b396d530d84e..8b36a2b1f87e 100644 --- a/pkg/detector/ospkg/ubuntu/ubuntu.go +++ b/pkg/detector/ospkg/ubuntu/ubuntu.go @@ -62,6 +62,7 @@ var ( "23.04": time.Date(2024, 1, 20, 23, 59, 59, 0, time.UTC), "23.10": time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC), "24.04": time.Date(2034, 3, 31, 23, 59, 59, 0, time.UTC), + "24.10": time.Date(2025, 7, 9, 23, 59, 59, 0, time.UTC), } ) From a16b830e00bac47ca61bcb929e0b4b89884953e9 Mon Sep 17 00:00:00 2001 From: afdesk Date: Fri, 25 Oct 2024 12:07:30 +0600 Subject: [PATCH 053/108] ci(helm): auto public Helm chart after PR merged (#7526) --- .github/workflows/bypass-test.yaml | 2 + .github/workflows/publish-chart.yaml | 46 +++++++++-- .github/workflows/test.yaml | 1 + magefiles/helm.go | 117 +++++++++++++++++++++++++++ magefiles/helm_test.go | 92 +++++++++++++++++++++ magefiles/magefile.go | 7 ++ 6 files changed, 260 insertions(+), 5 deletions(-) create mode 100644 magefiles/helm.go create mode 100644 magefiles/helm_test.go diff --git a/.github/workflows/bypass-test.yaml b/.github/workflows/bypass-test.yaml index 3a3102e3e574..93c5ba2869d3 100644 --- a/.github/workflows/bypass-test.yaml +++ b/.github/workflows/bypass-test.yaml @@ -9,6 +9,7 @@ on: - 'mkdocs.yml' - 'LICENSE' - '.release-please-manifest.json' + - 'helm/trivy/Chart.yaml' pull_request: paths: - '**.md' @@ -16,6 +17,7 @@ on: - 'mkdocs.yml' - 'LICENSE' - '.release-please-manifest.json' + - 'helm/trivy/Chart.yaml' jobs: test: name: Test diff --git a/.github/workflows/publish-chart.yaml b/.github/workflows/publish-chart.yaml index 3a7db4970065..15c8d84da31d 100644 --- a/.github/workflows/publish-chart.yaml +++ b/.github/workflows/publish-chart.yaml @@ -4,6 +4,11 @@ name: Publish Helm chart on: workflow_dispatch: pull_request: + types: + - opened + - synchronize + - reopened + - closed branches: - main paths: @@ -18,8 +23,10 @@ env: KIND_VERSION: "v0.14.0" KIND_IMAGE: "kindest/node:v1.23.6@sha256:b1fa224cc6c7ff32455e0b1fd9cbfd3d3bc87ecaa8fcb06961ed1afb3db0f9ae" jobs: + # `test-chart` job starts if a PR with Helm Chart is created, merged etc. test-chart: - runs-on: ubuntu-20.04 + if: github.event_name != 'push' + runs-on: ubuntu-24.04 steps: - name: Checkout uses: actions/checkout@v4.1.6 @@ -28,11 +35,12 @@ jobs: - name: Install Helm uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 with: - version: v3.5.0 + version: v3.14.4 - name: Set up python uses: actions/setup-python@v5 with: - python-version: 3.7 + python-version: '3.x' + check-latest: true - name: Setup Chart Linting id: lint uses: helm/chart-testing-action@e6669bcd63d7cb57cb4380c33043eebe5d111992 @@ -48,11 +56,39 @@ jobs: sed -i -e '136s,false,'true',g' ./helm/trivy/values.yaml ct lint-and-install --validate-maintainers=false --charts helm/trivy + # `update-chart-version` job starts if a new tag is pushed + update-chart-version: + if: github.event_name == 'push' + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@v4.1.6 + with: + fetch-depth: 0 + - name: Set up Git user + run: | + git config --global user.email "actions@github.com" + git config --global user.name "GitHub Actions" + + - name: Install tools + uses: aquaproj/aqua-installer@v3.0.1 + with: + aqua_version: v1.25.0 + aqua_opts: "" + + - name: Create a PR with Trivy version + run: mage helm:updateVersion + env: + # Use ORG_REPO_TOKEN instead of GITHUB_TOKEN + # This allows the created PR to trigger tests and other workflows + GITHUB_TOKEN: ${{ secrets.ORG_REPO_TOKEN }} + + # `publish-chart` job starts if a PR with a new Helm Chart is merged or manually publish-chart: - if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' + if: github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' needs: - test-chart - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - name: Checkout uses: actions/checkout@v4.1.6 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 8932a683c5bc..05edadf93331 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -7,6 +7,7 @@ on: - 'mkdocs.yml' - 'LICENSE' - '.release-please-manifest.json' ## don't run tests for release-please PRs + - 'helm/trivy/Chart.yaml' merge_group: workflow_dispatch: diff --git a/magefiles/helm.go b/magefiles/helm.go new file mode 100644 index 000000000000..443c57eed029 --- /dev/null +++ b/magefiles/helm.go @@ -0,0 +1,117 @@ +//go:build mage_helm + +package main + +import ( + "fmt" + "log" + "os" + + "github.com/aquasecurity/go-version/pkg/semver" + + "github.com/magefile/mage/sh" + "golang.org/x/xerrors" + "gopkg.in/yaml.v3" +) + +const chartFile = "./helm/trivy/Chart.yaml" + +func main() { + trivyVersion, err := version() + if err != nil { + log.Fatalf("could not determine Trivy version: %v", err) + } + + newHelmVersion, err := bumpHelmChart(chartFile, trivyVersion) + if err != nil { + log.Fatalf("could not bump Trivy version to %q: %v", trivyVersion, err) + } + + log.Printf("Current helm version will bump up %q with Trivy %q", newHelmVersion, trivyVersion) + + newBranch := fmt.Sprintf("ci/helm-chart/bump-trivy-to-%s", trivyVersion) + title := fmt.Sprintf("ci(helm): bump Trivy version to %s for Trivy Helm Chart %s", trivyVersion, newHelmVersion) + description := fmt.Sprintf("This PR bumps Trivy up to the %s version for the Trivy Helm chart %s.", + trivyVersion, newHelmVersion) + + cmds := [][]string{ + []string{"git", "switch", "-c", newBranch}, + []string{"git", "add", chartFile}, + []string{"git", "commit", "-m", title}, + []string{"git", "push", "origin", newBranch}, + []string{"gh", "pr", "create", "--base", "main", "--head", newBranch, "--title", title, "--body", description, "--repo", "$GITHUB_REPOSITORY"}, + } + + if err := runShCommands(cmds); err != nil { + log.Fatal(err) + } + log.Print("Successfully created PR with a new helm version") +} + +type Chart struct { + Version string `yaml:"version"` + AppVersion string `yaml:"appVersion"` +} + +// bumpHelmChart bumps up helm and trivy versions inside a file (Chart.yaml) +// it returns a new helm version and error +func bumpHelmChart(filename, trivyVersion string) (string, error) { + input, err := os.ReadFile(filename) + if err != nil { + return "", xerrors.Errorf("could not read file %q: %w", filename, err) + } + currentHelmChart := &Chart{} + if err := yaml.Unmarshal(input, currentHelmChart); err != nil { + return "", xerrors.Errorf("could not unmarshal helm chart %q: %w", filename, err) + } + + newHelmVersion, err := buildNewHelmVersion(currentHelmChart.Version, currentHelmChart.AppVersion, trivyVersion) + if err != nil { + return "", xerrors.Errorf("could not build new helm version: %v", err) + } + cmds := [][]string{ + []string{"sed", "-i", "-e", fmt.Sprintf("s/appVersion: %s/appVersion: %s/g", currentHelmChart.AppVersion, trivyVersion), filename}, + []string{"sed", "-i", "-e", fmt.Sprintf("s/version: %s/version: %s/g", currentHelmChart.Version, newHelmVersion), filename}, + } + + if err := runShCommands(cmds); err != nil { + return "", xerrors.Errorf("could not update Helm Chart %q: %w", newHelmVersion, err) + } + return newHelmVersion, nil +} + +func runShCommands(cmds [][]string) error { + for _, cmd := range cmds { + if err := sh.Run(cmd[0], cmd[1:]...); err != nil { + return xerrors.Errorf("failed to run %v: %w", cmd, err) + } + } + return nil +} + +func buildNewHelmVersion(currentHelm, currentTrivy, newTrivy string) (string, error) { + currentHelmVersion, err := semver.Parse(currentHelm) + if err != nil { + return "", xerrors.Errorf("could not parse current helm version: %w", err) + } + + currentTrivyVersion, err := semver.Parse(currentTrivy) + if err != nil { + return "", xerrors.Errorf("could not parse current trivy version: %w", err) + } + + newTrivyVersion, err := semver.Parse(newTrivy) + if err != nil { + return "", xerrors.Errorf("could not parse new trivy version: %w", err) + } + + if newTrivyVersion.Major().Compare(currentTrivyVersion.Major()) > 0 { + return currentHelmVersion.IncMajor().String(), nil + } + + if newTrivyVersion.Minor().Compare(currentTrivyVersion.Minor()) > 0 { + return currentHelmVersion.IncMinor().String(), nil + } + + return currentHelmVersion.IncPatch().String(), nil +} diff --git a/magefiles/helm_test.go b/magefiles/helm_test.go new file mode 100644 index 000000000000..f2e3233d2879 --- /dev/null +++ b/magefiles/helm_test.go @@ -0,0 +1,92 @@ +//go:build mage_helm + +package main + +import ( + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewVersion(t *testing.T) { + tests := []struct { + name string + currentHelmVersion string + currentTrivyVersion string + newTrivyVersion string + newHelmVersion string + }{ + { + "created the first patch", + "0.1.0", + "0.55.0", + "0.55.1", + "0.1.1", + }, + { + "created the second patch", + "0.1.1", + "0.55.1", + "0.55.2", + "0.1.2", + }, + { + "created the second patch but helm chart was changed", + "0.1.2", + "0.55.1", + "0.55.2", + "0.1.3", + }, + { + "created a new minor version", + "0.1.1", + "0.55.1", + "0.56.0", + "0.2.0", + }, + { + "created a new major version", + "0.1.1", + "0.55.1", + "1.0.0", + "1.0.0", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + newHelmVersion, err := buildNewHelmVersion(test.currentHelmVersion, test.currentTrivyVersion, test.newTrivyVersion) + assert.NoError(t, err) + assert.Equal(t, test.newHelmVersion, newHelmVersion) + }) + } +} + +func TestBumpHelmChart_Success(t *testing.T) { + tempFile, err := os.CreateTemp(t.TempDir(), "Chart-*.yaml") + assert.NoError(t, err) + + content := ` +apiVersion: v2 +name: trivy +version: 0.8.0 +appVersion: 0.55.0 +description: Trivy helm chart +keywords: + - scanner + - trivy + - vulnerability +` + err = os.WriteFile(tempFile.Name(), []byte(content), 0644) + assert.NoError(t, err) + + newVersion, err := bumpHelmChart(tempFile.Name(), "0.55.1") + assert.NoError(t, err) + assert.Equal(t, "0.8.1", newVersion) + + updatedContent, err := os.ReadFile(tempFile.Name()) + assert.NoError(t, err) + assert.Contains(t, string(updatedContent), "appVersion: 0.55.1") + assert.Contains(t, string(updatedContent), "version: 0.8.1") +} diff --git a/magefiles/magefile.go b/magefiles/magefile.go index 06ebd7e665e1..f70491bce2e8 100644 --- a/magefiles/magefile.go +++ b/magefiles/magefile.go @@ -489,3 +489,10 @@ func (CloudActions) Generate() error { func VEX(_ context.Context, dir string) error { return sh.RunWith(ENV, "go", "run", "-tags=mage_vex", "./magefiles/vex.go", "--dir", dir) } + +type Helm mg.Namespace + +// UpdateVersion updates a version for Trivy Helm Chart and creates a PR +func (Helm) UpdateVersion() error { + return sh.RunWith(ENV, "go", "run", "-tags=mage_helm", "./magefiles") +} From 63dd3d65a32296eee529c41c41f3221e2cd6d3fe Mon Sep 17 00:00:00 2001 From: okamototk Date: Mon, 28 Oct 2024 15:26:13 +0900 Subject: [PATCH 054/108] docs: add Windows install instructions (#7800) --- docs/getting-started/installation.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index fd8b19366e86..6e3c8cd0cfd2 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -56,6 +56,13 @@ Homebrew for MacOS and Linux. brew install trivy ``` +### Windows (Official) + +1. Download trivy_x.xx.x_windows-64bit.zip file from [releases page](https://github.com/aquasecurity/trivy/releases/). +2. Unzip file and copy to any folder. +3. Ensure PATH environment variable is configured to folder trivy installed. + + ### Arch Linux (Community) Arch Linux Package Repository. From 7a4f4d8b12996687f3095a2042cdf2f5985332c9 Mon Sep 17 00:00:00 2001 From: afdesk Date: Tue, 29 Oct 2024 13:08:13 +0600 Subject: [PATCH 055/108] fix(k8s): support kubernetes v1.31 (#7810) --- go.mod | 16 ++++++++-------- go.sum | 32 ++++++++++++++++---------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index 1611c8c7080a..efc9daa926c9 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/aquasecurity/trivy-checks v1.1.0 github.com/aquasecurity/trivy-db v0.0.0-20240910133327-7e0f4d2ed4c1 github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 - github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20240707095038-0300bc49b68b + github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20241029051843-2606b7e0f0b4 github.com/aws/aws-sdk-go-v2 v1.31.0 github.com/aws/aws-sdk-go-v2/config v1.27.38 github.com/aws/aws-sdk-go-v2/credentials v1.17.36 @@ -131,7 +131,7 @@ require ( google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.16.1 - k8s.io/api v0.31.0 + k8s.io/api v0.31.2 k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 modernc.org/sqlite v1.33.1 sigs.k8s.io/yaml v1.4.0 @@ -173,7 +173,7 @@ require ( github.com/antchfx/xpath v1.3.1 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/aws/aws-sdk-go v1.54.6 // indirect + github.com/aws/aws-sdk-go v1.55.5 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 // indirect @@ -402,14 +402,14 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/apiextensions-apiserver v0.31.0 // indirect - k8s.io/apimachinery v0.31.0 // indirect + k8s.io/apimachinery v0.31.2 // indirect k8s.io/apiserver v0.31.0 // indirect - k8s.io/cli-runtime v0.31.0 // indirect - k8s.io/client-go v0.31.0 // indirect - k8s.io/component-base v0.31.0 // indirect + k8s.io/cli-runtime v0.31.2 // indirect + k8s.io/client-go v0.31.2 // indirect + k8s.io/component-base v0.31.2 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect - k8s.io/kubectl v0.31.0 // indirect + k8s.io/kubectl v0.31.2 // indirect modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect modernc.org/libc v1.55.3 // indirect modernc.org/mathutil v1.6.0 // indirect diff --git a/go.sum b/go.sum index 51d43bf95aa1..153c1ab02dea 100644 --- a/go.sum +++ b/go.sum @@ -355,8 +355,8 @@ github.com/aquasecurity/trivy-db v0.0.0-20240910133327-7e0f4d2ed4c1 h1:G0gnacAOR github.com/aquasecurity/trivy-db v0.0.0-20240910133327-7e0f4d2ed4c1/go.mod h1:PYkSRx4dlgFATEt+okGwibvbxVEtqsOdH+vX/saACYE= github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 h1:JVgBIuIYbwG+ekC5lUHUpGJboPYiCcxiz06RCtz8neI= github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48/go.mod h1:Ldya37FLi0e/5Cjq2T5Bty7cFkzUDwTcPeQua+2M8i8= -github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20240707095038-0300bc49b68b h1:h7gsIzHyrxpQnayOuQI0kX7+8rVcqhV6G5bM3KVFyJU= -github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20240707095038-0300bc49b68b/go.mod h1:HOhrqoyIeTxpwnKr1EyWtQ+rt2XahV8b0UDBrRpSfEQ= +github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20241029051843-2606b7e0f0b4 h1:i0Z0JS4xtMAcBVOpYSciS7slmIBi1SmjT6garbrJtcA= +github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20241029051843-2606b7e0f0b4/go.mod h1:ctlibFXOQyjWybeVVQI6NLG6GJoPWZJ4cIirQ/wPCQs= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -364,8 +364,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= -github.com/aws/aws-sdk-go v1.54.6 h1:HEYUib3yTt8E6vxjMWM3yAq5b+qjj/6aKA62mkgux9g= -github.com/aws/aws-sdk-go v1.54.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.31.0 h1:3V05LbxTSItI5kUqNwhJrrrY1BAXxXt0sN0l72QmG5U= github.com/aws/aws-sdk-go-v2 v1.31.0/go.mod h1:ztolYtaEUtdpf9Wftr31CJfLVjOnD/CVRkKOOYgF8hA= github.com/aws/aws-sdk-go-v2/config v1.27.38 h1:mMVyJJuSUdbD4zKXoxDgWrgM60QwlFEg+JhihCq6wCw= @@ -2076,26 +2076,26 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo= -k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE= +k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0= +k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk= k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= -k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc= -k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw= +k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= k8s.io/apiserver v0.31.0 h1:p+2dgJjy+bk+B1Csz+mc2wl5gHwvNkC9QJV+w55LVrY= k8s.io/apiserver v0.31.0/go.mod h1:KI9ox5Yu902iBnnyMmy7ajonhKnkeZYJhTZ/YI+WEMk= -k8s.io/cli-runtime v0.31.0 h1:V2Q1gj1u3/WfhD475HBQrIYsoryg/LrhhK4RwpN+DhA= -k8s.io/cli-runtime v0.31.0/go.mod h1:vg3H94wsubuvWfSmStDbekvbla5vFGC+zLWqcf+bGDw= -k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8= -k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU= -k8s.io/component-base v0.31.0 h1:/KIzGM5EvPNQcYgwq5NwoQBaOlVFrghoVGr8lG6vNRs= -k8s.io/component-base v0.31.0/go.mod h1:TYVuzI1QmN4L5ItVdMSXKvH7/DtvIuas5/mm8YT3rTo= +k8s.io/cli-runtime v0.31.2 h1:7FQt4C4Xnqx8V1GJqymInK0FFsoC+fAZtbLqgXYVOLQ= +k8s.io/cli-runtime v0.31.2/go.mod h1:XROyicf+G7rQ6FQJMbeDV9jqxzkWXTYD6Uxd15noe0Q= +k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc= +k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs= +k8s.io/component-base v0.31.2 h1:Z1J1LIaC0AV+nzcPRFqfK09af6bZ4D1nAOpWsy9owlA= +k8s.io/component-base v0.31.2/go.mod h1:9PeyyFN/drHjtJZMCTkSpQJS3U9OXORnHQqMLDz0sUQ= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= -k8s.io/kubectl v0.31.0 h1:kANwAAPVY02r4U4jARP/C+Q1sssCcN/1p9Nk+7BQKVg= -k8s.io/kubectl v0.31.0/go.mod h1:pB47hhFypGsaHAPjlwrNbvhXgmuAr01ZBvAIIUaI8d4= +k8s.io/kubectl v0.31.2 h1:gTxbvRkMBwvTSAlobiTVqsH6S8Aa1aGyBcu5xYLsn8M= +k8s.io/kubectl v0.31.2/go.mod h1:EyASYVU6PY+032RrTh5ahtSOMgoDRIux9V1JLKtG5xM= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= From c4347759234dcb5f372b07f92fb4230ef391d710 Mon Sep 17 00:00:00 2001 From: smtan-gl <139748976+smtan-gl@users.noreply.github.com> Date: Tue, 29 Oct 2024 15:26:28 +0800 Subject: [PATCH 056/108] fix(k8s)!: support k8s multi container (#7444) Co-authored-by: afdesk --- pkg/k8s/report/report.go | 16 +-- pkg/k8s/report/report_test.go | 185 ++++++++++++++++++++++++++++------ 2 files changed, 165 insertions(+), 36 deletions(-) diff --git a/pkg/k8s/report/report.go b/pkg/k8s/report/report.go index e71c218bf864..b52391ec8239 100644 --- a/pkg/k8s/report/report.go +++ b/pkg/k8s/report/report.go @@ -57,9 +57,9 @@ type Resource struct { Namespace string `json:",omitempty"` Kind string Name string - Metadata types.Metadata `json:",omitempty"` - Results types.Results `json:",omitempty"` - Error string `json:",omitempty"` + Metadata []types.Metadata `json:",omitempty"` + Results types.Results `json:",omitempty"` + Error string `json:",omitempty"` // original report Report types.Report `json:"-"` @@ -99,11 +99,15 @@ func (r Report) consolidate() ConsolidatedReport { key := v.fullname() if res, ok := index[key]; ok { + // Combine metadata + metadata := lo.UniqBy(append(res.Metadata, v.Metadata...), func(x types.Metadata) string { + return x.ImageID + }) index[key] = Resource{ Namespace: res.Namespace, Kind: res.Kind, Name: res.Name, - Metadata: res.Metadata, + Metadata: metadata, Results: append(res.Results, v.Results...), Error: res.Error, } @@ -214,7 +218,7 @@ func infraResource(misConfig Resource) bool { func CreateResource(artifact *artifacts.Artifact, report types.Report, err error) Resource { r := createK8sResource(artifact, report.Results) - r.Metadata = report.Metadata + r.Metadata = []types.Metadata{report.Metadata} r.Report = report // if there was any error during the scan if err != nil { @@ -244,7 +248,7 @@ func createK8sResource(artifact *artifacts.Artifact, scanResults types.Results) Namespace: artifact.Namespace, Kind: artifact.Kind, Name: artifact.Name, - Metadata: types.Metadata{}, + Metadata: []types.Metadata{}, Results: results, Report: types.Report{ Results: results, diff --git a/pkg/k8s/report/report_test.go b/pkg/k8s/report/report_test.go index e9e9ae5e3a91..1a984eb71b23 100644 --- a/pkg/k8s/report/report_test.go +++ b/pkg/k8s/report/report_test.go @@ -14,12 +14,15 @@ var ( Namespace: "default", Kind: "Deploy", Name: "orion", - Metadata: types.Metadata{ - RepoTags: []string{ - "alpine:3.14", - }, - RepoDigests: []string{ - "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + Metadata: []types.Metadata{ + { + ImageID: "123", + RepoTags: []string{ + "alpine:3.14", + }, + RepoDigests: []string{ + "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + }, }, }, Results: types.Results{ @@ -69,12 +72,15 @@ var ( Namespace: "default", Kind: "Deploy", Name: "orion", - Metadata: types.Metadata{ - RepoTags: []string{ - "alpine:3.14", - }, - RepoDigests: []string{ - "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + Metadata: []types.Metadata{ + { + ImageID: "123", + RepoTags: []string{ + "alpine:3.14", + }, + RepoDigests: []string{ + "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + }, }, }, Results: types.Results{ @@ -113,16 +119,117 @@ var ( }, } + image1WithVulns = Resource{ + Namespace: "default", + Kind: "Pod", + Name: "multi-image-pod", + Metadata: []types.Metadata{ + { + ImageID: "image1", + RepoTags: []string{ + "alpine:3.14", + }, + RepoDigests: []string{ + "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + }, + }, + }, + Results: types.Results{ + { + Vulnerabilities: []types.DetectedVulnerability{ + { + VulnerabilityID: "CVE-2022-1111", + Vulnerability: dbTypes.Vulnerability{Severity: "LOW"}, + }, + }, + }, + }, + } + + image2WithVulns = Resource{ + Namespace: "default", + Kind: "Pod", + Name: "multi-image-pod", + Metadata: []types.Metadata{ + { + ImageID: "image2", + RepoTags: []string{ + "alpine:3.17.3", + }, + RepoDigests: []string{ + "alpine@sha256:124c7d2707904eea7431fffe91522a01e5a861a624ee31d03372cc1d138a3126", + }, + }, + }, + Results: types.Results{ + { + Vulnerabilities: []types.DetectedVulnerability{ + { + VulnerabilityID: "CVE-2022-2222", + Vulnerability: dbTypes.Vulnerability{Severity: "MEDIUM"}, + }, + }, + }, + }, + } + + multiImagePodWithVulns = Resource{ + Namespace: "default", + Kind: "Pod", + Name: "multi-image-pod", + Metadata: []types.Metadata{ + { + ImageID: "image1", + RepoTags: []string{ + "alpine:3.14", + }, + RepoDigests: []string{ + "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + }, + }, + { + ImageID: "image2", + RepoTags: []string{ + "alpine:3.17.3", + }, + RepoDigests: []string{ + "alpine@sha256:124c7d2707904eea7431fffe91522a01e5a861a624ee31d03372cc1d138a3126", + }, + }, + }, + Results: types.Results{ + { + Vulnerabilities: []types.DetectedVulnerability{ + { + VulnerabilityID: "CVE-2022-1111", + Vulnerability: dbTypes.Vulnerability{Severity: "LOW"}, + }, + }, + }, + { + Vulnerabilities: []types.DetectedVulnerability{ + { + VulnerabilityID: "CVE-2022-2222", + Vulnerability: dbTypes.Vulnerability{Severity: "MEDIUM"}, + }, + }, + }, + }, + } + deployOrionWithBothVulnsAndMisconfigs = Resource{ Namespace: "default", Kind: "Deploy", Name: "orion", - Metadata: types.Metadata{ - RepoTags: []string{ - "alpine:3.14", - }, - RepoDigests: []string{ - "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + Metadata: []types.Metadata{ + { + ImageID: "123", + RepoTags: []string{ + "alpine:3.14", + }, + RepoDigests: []string{ + "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + }, }, }, Results: types.Results{ @@ -204,12 +311,15 @@ var ( Namespace: "default", Kind: "Cronjob", Name: "hello", - Metadata: types.Metadata{ - RepoTags: []string{ - "alpine:3.14", - }, - RepoDigests: []string{ - "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + Metadata: []types.Metadata{ + { + ImageID: "123", + RepoTags: []string{ + "alpine:3.14", + }, + RepoDigests: []string{ + "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + }, }, }, Results: types.Results{ @@ -221,12 +331,15 @@ var ( Namespace: "default", Kind: "Pod", Name: "prometheus", - Metadata: types.Metadata{ - RepoTags: []string{ - "alpine:3.14", - }, - RepoDigests: []string{ - "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + Metadata: []types.Metadata{ + { + ImageID: "123", + RepoTags: []string{ + "alpine:3.14", + }, + RepoDigests: []string{ + "alpine:3.14@sha256:8fe1727132b2506c17ba0e1f6a6ed8a016bb1f5735e43b2738cd3fd1979b6260", + }, }, }, Results: types.Results{ @@ -358,6 +471,18 @@ func TestReport_consolidate(t *testing.T) { "default/cronjob/hello": cronjobHelloWithVulns, }, }, + { + name: "report with multi image pod containing vulnerabilities", + report: Report{ + Resources: []Resource{ + image1WithVulns, + image2WithVulns, + }, + }, + expectedFindings: map[string]Resource{ + "default/pod/multi-image-pod": multiImagePodWithVulns, + }, + }, } for _, tt := range tests { From c70b6fa16613fa5482fe4ece9859b2cf60752977 Mon Sep 17 00:00:00 2001 From: simar7 <1254783+simar7@users.noreply.github.com> Date: Tue, 29 Oct 2024 16:29:42 -0600 Subject: [PATCH 057/108] refactor(misconf): Deprecate `EXCEPTIONS` for misconfiguration scanning (#7776) Signed-off-by: Simar --- contrib/junit.tpl | 2 +- docs/docs/configuration/filtering.md | 6 +- .../configuration/cli/trivy_config.md | 2 +- .../configuration/cli/trivy_filesystem.md | 2 +- .../configuration/cli/trivy_image.md | 2 +- .../configuration/cli/trivy_kubernetes.md | 2 +- .../configuration/cli/trivy_repository.md | 2 +- .../configuration/cli/trivy_rootfs.md | 2 +- .../references/configuration/cli/trivy_vm.md | 2 +- .../misconfiguration/check/exceptions.md | 92 --- .../scanner/misconfiguration/custom/debug.md | 2 +- .../scanner/misconfiguration/custom/index.md | 2 +- docs/docs/scanner/misconfiguration/index.md | 22 +- docs/docs/target/container_image.md | 2 +- docs/docs/target/kubernetes.md | 3 +- integration/repo_test.go | 9 - .../dockerfile-custom-policies.json.golden | 3 +- ...dockerfile-namespace-exception.json.golden | 30 - .../dockerfile-rule-exception.json.golden | 3 +- integration/testdata/dockerfile.json.golden | 3 +- .../dockerfile_file_pattern.json.golden | 3 +- .../repo/namespace-exception/Dockerfile | 2 - .../namespace-exception/policy/exception.rego | 8 - integration/testdata/helm.json.golden | 3 +- .../testdata/helm_testchart.json.golden | 9 +- .../helm_testchart.overridden.json.golden | 9 +- mkdocs.yml | 1 - pkg/cache/mock_artifact_cache.go | 3 - pkg/compliance/report/report_test.go | 10 +- pkg/compliance/report/testdata/table_all.txt | 24 +- pkg/compliance/spec/mapper.go | 2 - pkg/compliance/spec/mapper_test.go | 10 +- pkg/fanal/types/misconf.go | 14 +- pkg/flag/misconf_flags.go | 2 +- pkg/iac/rego/exceptions.go | 35 - pkg/iac/rego/scanner.go | 25 +- pkg/iac/rego/scanner_test.go | 162 ----- pkg/iac/scanners/cloudformation/scanner.go | 1 + .../scanners/terraform/executor/executor.go | 1 + pkg/misconf/scanner.go | 2 - pkg/report/table/misconfig.go | 4 +- pkg/report/table/misconfig_test.go | 19 +- pkg/result/filter.go | 6 +- pkg/result/filter_test.go | 20 +- pkg/rpc/convert.go | 24 +- pkg/scanner/local/scan.go | 3 - pkg/scanner/local/scan_test.go | 23 - pkg/types/report.go | 7 +- rpc/cache/service.pb.go | 2 +- rpc/common/service.pb.go | 665 +++++++++--------- rpc/common/service.proto | 3 +- 51 files changed, 430 insertions(+), 865 deletions(-) delete mode 100644 docs/docs/scanner/misconfiguration/check/exceptions.md delete mode 100644 integration/testdata/dockerfile-namespace-exception.json.golden delete mode 100644 integration/testdata/fixtures/repo/namespace-exception/Dockerfile delete mode 100644 integration/testdata/fixtures/repo/namespace-exception/policy/exception.rego delete mode 100644 pkg/iac/rego/exceptions.go diff --git a/contrib/junit.tpl b/contrib/junit.tpl index b01bb9fd22dd..27b654b7b049 100644 --- a/contrib/junit.tpl +++ b/contrib/junit.tpl @@ -16,7 +16,7 @@ {{- if .MisconfSummary }} - + {{- else }} {{- end }} diff --git a/docs/docs/configuration/filtering.md b/docs/docs/configuration/filtering.md index befcc54180ac..22fbe2d7a24d 100644 --- a/docs/docs/configuration/filtering.md +++ b/docs/docs/configuration/filtering.md @@ -112,7 +112,7 @@ trivy config --severity HIGH,CRITICAL examples/misconf/mixed Dockerfile (dockerfile) ======================= -Tests: 17 (SUCCESSES: 16, FAILURES: 1, EXCEPTIONS: 0) +Tests: 17 (SUCCESSES: 16, FAILURES: 1) Failures: 1 (HIGH: 1, CRITICAL: 0) HIGH: Last USER command in Dockerfile should not be 'root' @@ -130,13 +130,13 @@ See https://avd.aquasec.com/misconfig/ds002 deployment.yaml (kubernetes) ============================ -Tests: 8 (SUCCESSES: 8, FAILURES: 0, EXCEPTIONS: 0) +Tests: 8 (SUCCESSES: 8, FAILURES: 0) Failures: 0 (HIGH: 0, CRITICAL: 0) main.tf (terraform) =================== -Tests: 1 (SUCCESSES: 0, FAILURES: 1, EXCEPTIONS: 0) +Tests: 1 (SUCCESSES: 0, FAILURES: 1) Failures: 1 (HIGH: 0, CRITICAL: 1) CRITICAL: Classic resources should not be used. diff --git a/docs/docs/references/configuration/cli/trivy_config.md b/docs/docs/references/configuration/cli/trivy_config.md index 0c279f1e7bf3..804b33725522 100644 --- a/docs/docs/references/configuration/cli/trivy_config.md +++ b/docs/docs/references/configuration/cli/trivy_config.md @@ -32,7 +32,7 @@ trivy config [flags] DIR --ignore-policy string specify the Rego file path to evaluate each vulnerability --ignorefile string specify .trivyignore file (default ".trivyignore") --include-deprecated-checks include deprecated checks - --include-non-failures include successes and exceptions, available with '--scanners misconfig' + --include-non-failures include successes, available with '--scanners misconfig' --k8s-version string specify k8s version to validate outdated api by it (example: 1.21.0) --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) --module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules") diff --git a/docs/docs/references/configuration/cli/trivy_filesystem.md b/docs/docs/references/configuration/cli/trivy_filesystem.md index bbd5257d5f5d..dd1b2cd7a2e4 100644 --- a/docs/docs/references/configuration/cli/trivy_filesystem.md +++ b/docs/docs/references/configuration/cli/trivy_filesystem.md @@ -55,7 +55,7 @@ trivy filesystem [flags] PATH --ignorefile string specify .trivyignore file (default ".trivyignore") --include-deprecated-checks include deprecated checks --include-dev-deps include development dependencies in the report (supported: npm, yarn) - --include-non-failures include successes and exceptions, available with '--scanners misconfig' + --include-non-failures include successes, available with '--scanners misconfig' --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) --license-confidence-level float specify license classifier's confidence level (default 0.9) --license-full eagerly look for licenses in source code headers and license files diff --git a/docs/docs/references/configuration/cli/trivy_image.md b/docs/docs/references/configuration/cli/trivy_image.md index 779b54f4d5b9..4a76ca742cad 100644 --- a/docs/docs/references/configuration/cli/trivy_image.md +++ b/docs/docs/references/configuration/cli/trivy_image.md @@ -72,7 +72,7 @@ trivy image [flags] IMAGE_NAME --image-config-scanners strings comma-separated list of what security issues to detect on container image configurations (misconfig,secret) --image-src strings image source(s) to use, in priority order (docker,containerd,podman,remote) (default [docker,containerd,podman,remote]) --include-deprecated-checks include deprecated checks - --include-non-failures include successes and exceptions, available with '--scanners misconfig' + --include-non-failures include successes, available with '--scanners misconfig' --input string input file path instead of image name --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) --license-confidence-level float specify license classifier's confidence level (default 0.9) diff --git a/docs/docs/references/configuration/cli/trivy_kubernetes.md b/docs/docs/references/configuration/cli/trivy_kubernetes.md index 6b8058cc00f0..1a25e859c3c8 100644 --- a/docs/docs/references/configuration/cli/trivy_kubernetes.md +++ b/docs/docs/references/configuration/cli/trivy_kubernetes.md @@ -69,7 +69,7 @@ trivy kubernetes [flags] [CONTEXT] --include-deprecated-checks include deprecated checks --include-kinds strings indicate the kinds included in scanning (example: node) --include-namespaces strings indicate the namespaces included in scanning (example: kube-system) - --include-non-failures include successes and exceptions, available with '--scanners misconfig' + --include-non-failures include successes, available with '--scanners misconfig' --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) --k8s-version string specify k8s version to validate outdated api by it (example: 1.21.0) --kubeconfig string specify the kubeconfig file path to use diff --git a/docs/docs/references/configuration/cli/trivy_repository.md b/docs/docs/references/configuration/cli/trivy_repository.md index 190640f5cb2a..5936e0c34a61 100644 --- a/docs/docs/references/configuration/cli/trivy_repository.md +++ b/docs/docs/references/configuration/cli/trivy_repository.md @@ -55,7 +55,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL) --ignorefile string specify .trivyignore file (default ".trivyignore") --include-deprecated-checks include deprecated checks --include-dev-deps include development dependencies in the report (supported: npm, yarn) - --include-non-failures include successes and exceptions, available with '--scanners misconfig' + --include-non-failures include successes, available with '--scanners misconfig' --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) --license-confidence-level float specify license classifier's confidence level (default 0.9) --license-full eagerly look for licenses in source code headers and license files diff --git a/docs/docs/references/configuration/cli/trivy_rootfs.md b/docs/docs/references/configuration/cli/trivy_rootfs.md index 2f37484d0328..acd1d529a8ef 100644 --- a/docs/docs/references/configuration/cli/trivy_rootfs.md +++ b/docs/docs/references/configuration/cli/trivy_rootfs.md @@ -57,7 +57,7 @@ trivy rootfs [flags] ROOTDIR --ignored-licenses strings specify a list of license to ignore --ignorefile string specify .trivyignore file (default ".trivyignore") --include-deprecated-checks include deprecated checks - --include-non-failures include successes and exceptions, available with '--scanners misconfig' + --include-non-failures include successes, available with '--scanners misconfig' --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) --license-confidence-level float specify license classifier's confidence level (default 0.9) --license-full eagerly look for licenses in source code headers and license files diff --git a/docs/docs/references/configuration/cli/trivy_vm.md b/docs/docs/references/configuration/cli/trivy_vm.md index 14ce8b78c21a..d4e3692f7e1c 100644 --- a/docs/docs/references/configuration/cli/trivy_vm.md +++ b/docs/docs/references/configuration/cli/trivy_vm.md @@ -51,7 +51,7 @@ trivy vm [flags] VM_IMAGE --ignore-status strings comma-separated list of vulnerability status to ignore (unknown,not_affected,affected,fixed,under_investigation,will_not_fix,fix_deferred,end_of_life) --ignore-unfixed display only fixed vulnerabilities --ignorefile string specify .trivyignore file (default ".trivyignore") - --include-non-failures include successes and exceptions, available with '--scanners misconfig' + --include-non-failures include successes, available with '--scanners misconfig' --java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1]) --list-all-pkgs output all packages in the JSON report regardless of vulnerability --misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot]) diff --git a/docs/docs/scanner/misconfiguration/check/exceptions.md b/docs/docs/scanner/misconfiguration/check/exceptions.md deleted file mode 100644 index 70ef974a4b74..000000000000 --- a/docs/docs/scanner/misconfiguration/check/exceptions.md +++ /dev/null @@ -1,92 +0,0 @@ -# Exceptions -Exceptions let you specify cases where you allow policy violations. -Trivy supports two types of exceptions. - -!!! info - Exceptions can be applied to built-in checks as well as custom checks. - -## Namespace-based exceptions -There are some cases where you need to disable built-in checks partially or fully. -Namespace-based exceptions lets you rough choose which individual packages to exempt. - -To use namespace-based exceptions, create a Rego rule with the name `exception` that returns the package names to exempt. -The `exception` rule must be defined under `namespace.exceptions`. -`data.namespaces` includes all package names. - - -!!! example - ``` rego - package namespace.exceptions - - import data.namespaces - - exception[ns] { - ns := data.namespaces[_] - startswith(ns, "builtin.kubernetes") - } - ``` - -This example exempts all built-in checks for Kubernetes. - -## Rule-based exceptions -There are some cases where you need more flexibility and granularity in defining which cases to exempt. -Rule-based exceptions lets you granularly choose which individual rules to exempt, while also declaring under which conditions to exempt them. - -To use rule-based exceptions, create a Rego rule with the name `exception` that returns the rule name suffixes to exempt, prefixed by `deny_` (for example, returning `foo` will exempt `deny_foo`). -The rule can make any other assertion, for example, on the input or data documents. -This is useful to specify the exemption for a specific case. - -Note that if you specify the empty string, the exception will match all rules named `deny`. - -``` -exception[rules] { - # Logic - - rules = ["foo","bar"] -} -``` - -The above would provide an exception from `deny_foo` and `deny_bar`. - - -!!! example - ``` - package user.kubernetes.ID100 - - __rego_metadata := { - "id": "ID100", - "title": "Deployment not allowed", - "severity": "HIGH", - "type": "Kubernetes Custom Check", - } - - deny_deployment[msg] { - input.kind == "Deployment" - msg = sprintf("Found deployment '%s' but deployments are not allowed", [name]) - } - - exception[rules] { - input.kind == "Deployment" - input.metadata.name == "allow-deployment" - - rules := ["deployment"] - } - ``` - -If you want to apply rule-based exceptions to built-in checks, you have to define the exception under the same package. - -!!! example - ``` rego - package builtin.kubernetes.KSV012 - - exception[rules] { - input.metadata.name == "can-run-as-root" - rules := [""] - } - ``` - -This exception is applied to [KSV012][ksv012] in trivy-checks. -You can get the package names in the [trivy-checks repository][trivy-checks] or the JSON output from Trivy. - -[ksv012]: https://github.com/aquasecurity/trivy-checks/blob/f36a5b732c4b1293a720c40baab0a7c106ea455e/checks/kubernetes/pss/restricted/3_runs_as_root.rego -[trivy-checks]: https://github.com/aquasecurity/trivy-checks/ \ No newline at end of file diff --git a/docs/docs/scanner/misconfiguration/custom/debug.md b/docs/docs/scanner/misconfiguration/custom/debug.md index 54ec9fd65273..bb7b506c7ece 100644 --- a/docs/docs/scanner/misconfiguration/custom/debug.md +++ b/docs/docs/scanner/misconfiguration/custom/debug.md @@ -12,7 +12,7 @@ $ trivy config --trace configs/ Dockerfile (dockerfile) ======================= -Tests: 23 (SUCCESSES: 21, FAILURES: 2, EXCEPTIONS: 0) +Tests: 23 (SUCCESSES: 21, FAILURES: 2) Failures: 2 (UNKNOWN: 0, LOW: 0, MEDIUM: 1, HIGH: 1, CRITICAL: 0) MEDIUM: Specify a tag in the 'FROM' statement for image 'alpine' diff --git a/docs/docs/scanner/misconfiguration/custom/index.md b/docs/docs/scanner/misconfiguration/custom/index.md index 9598089b8562..d45638f1533d 100644 --- a/docs/docs/scanner/misconfiguration/custom/index.md +++ b/docs/docs/scanner/misconfiguration/custom/index.md @@ -163,7 +163,7 @@ Some fields are displayed in scan results. k.yaml (kubernetes) ─────────────────── -Tests: 32 (SUCCESSES: 31, FAILURES: 1, EXCEPTIONS: 0) +Tests: 32 (SUCCESSES: 31, FAILURES: 1) Failures: 1 (UNKNOWN: 0, LOW: 1, MEDIUM: 0, HIGH: 0, CRITICAL: 0) LOW: Found deployment 'my-deployment' but deployments are not allowed diff --git a/docs/docs/scanner/misconfiguration/index.md b/docs/docs/scanner/misconfiguration/index.md index 368b4522b433..8a2f331b6ab0 100644 --- a/docs/docs/scanner/misconfiguration/index.md +++ b/docs/docs/scanner/misconfiguration/index.md @@ -20,7 +20,7 @@ $ trivy config [YOUR_IaC_DIRECTORY] Dockerfile (dockerfile) ======================= - Tests: 23 (SUCCESSES: 22, FAILURES: 1, EXCEPTIONS: 0) + Tests: 23 (SUCCESSES: 22, FAILURES: 1) Failures: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 1, HIGH: 0, CRITICAL: 0) MEDIUM: Specify a tag in the 'FROM' statement for image 'alpine' @@ -75,7 +75,7 @@ You can specify `--scanners vuln,misconfig,secret` to enable vulnerability and s Dockerfile (dockerfile) ======================= - Tests: 17 (SUCCESSES: 16, FAILURES: 1, EXCEPTIONS: 0) + Tests: 17 (SUCCESSES: 16, FAILURES: 1) Failures: 1 (HIGH: 1, CRITICAL: 0) HIGH: Last USER command in Dockerfile should not be 'root' @@ -112,7 +112,7 @@ $ trivy config --severity HIGH,CRITICAL ./iac Dockerfile (dockerfile) -Tests: 21 (SUCCESSES: 20, FAILURES: 1, EXCEPTIONS: 0) +Tests: 21 (SUCCESSES: 20, FAILURES: 1) Failures: 1 (MEDIUM: 0, HIGH: 1, CRITICAL: 0) HIGH: Specify at least 1 USER command in Dockerfile with non-root user as argument @@ -126,7 +126,7 @@ See https://avd.aquasec.com/misconfig/ds002 deployment.yaml (kubernetes) -Tests: 20 (SUCCESSES: 15, FAILURES: 5, EXCEPTIONS: 0) +Tests: 20 (SUCCESSES: 15, FAILURES: 5) Failures: 5 (MEDIUM: 4, HIGH: 1, CRITICAL: 0) MEDIUM: Container 'hello-kubernetes' of Deployment 'hello-kubernetes' should set 'securityContext.allowPrivilegeEscalation' to false @@ -225,7 +225,7 @@ See https://avd.aquasec.com/misconfig/ksv026 mysql-8.8.26.tar:templates/primary/statefulset.yaml (helm) -Tests: 20 (SUCCESSES: 18, FAILURES: 2, EXCEPTIONS: 0) +Tests: 20 (SUCCESSES: 18, FAILURES: 2) Failures: 2 (MEDIUM: 2, HIGH: 0, CRITICAL: 0) MEDIUM: Container 'mysql' of StatefulSet 'mysql' should set 'securityContext.allowPrivilegeEscalation' to false @@ -279,35 +279,35 @@ You can see the config type next to each file name. ``` bash Dockerfile (dockerfile) ======================= -Tests: 23 (SUCCESSES: 22, FAILURES: 1, EXCEPTIONS: 0) +Tests: 23 (SUCCESSES: 22, FAILURES: 1) Failures: 1 (HIGH: 1, CRITICAL: 0) ... deployment.yaml (kubernetes) ============================ -Tests: 28 (SUCCESSES: 15, FAILURES: 13, EXCEPTIONS: 0) +Tests: 28 (SUCCESSES: 15, FAILURES: 13) Failures: 13 (MEDIUM: 4, HIGH: 1, CRITICAL: 0) ... main.tf (terraform) =================== -Tests: 23 (SUCCESSES: 14, FAILURES: 9, EXCEPTIONS: 0) +Tests: 23 (SUCCESSES: 14, FAILURES: 9) Failures: 9 (HIGH: 6, CRITICAL: 1) ... bucket.yaml (cloudformation) ============================ -Tests: 9 (SUCCESSES: 3, FAILURES: 6, EXCEPTIONS: 0) +Tests: 9 (SUCCESSES: 3, FAILURES: 6) Failures: 6 (UNKNOWN: 0, LOW: 0, MEDIUM: 2, HIGH: 4, CRITICAL: 0) ... mysql-8.8.26.tar:templates/primary/statefulset.yaml (helm) ========================================================== -Tests: 20 (SUCCESSES: 18, FAILURES: 2, EXCEPTIONS: 0) +Tests: 20 (SUCCESSES: 18, FAILURES: 2) Failures: 2 (MEDIUM: 2, HIGH: 0, CRITICAL: 0) ``` @@ -381,7 +381,7 @@ deny[res] { $ trivy config --misconfig-scanners=json,yaml --config-check ./serverless.rego --check-namespaces user ./iac serverless.yaml (yaml) -Tests: 4 (SUCCESSES: 3, FAILURES: 1, EXCEPTIONS: 0) +Tests: 4 (SUCCESSES: 3, FAILURES: 1) Failures: 1 (UNKNOWN: 0, LOW: 1, MEDIUM: 0, HIGH: 0, CRITICAL: 0) LOW: Service name "serverless-rest-api-with-pynamodb" is not allowed diff --git a/docs/docs/target/container_image.md b/docs/docs/target/container_image.md index f1c491071a3f..274b43862339 100644 --- a/docs/docs/target/container_image.md +++ b/docs/docs/target/container_image.md @@ -119,7 +119,7 @@ $ trivy image --image-config-scanners misconfig [YOUR_IMAGE_NAME] ``` alpine:3.17 (dockerfile) ======================== -Tests: 24 (SUCCESSES: 21, FAILURES: 3, EXCEPTIONS: 0) +Tests: 24 (SUCCESSES: 21, FAILURES: 3) Failures: 3 (UNKNOWN: 0, LOW: 2, MEDIUM: 0, HIGH: 1, CRITICAL: 0) HIGH: Specify at least 1 USER command in Dockerfile with non-root user as argument diff --git a/docs/docs/target/kubernetes.md b/docs/docs/target/kubernetes.md index 10253aff4c72..291c9fb7728e 100644 --- a/docs/docs/target/kubernetes.md +++ b/docs/docs/target/kubernetes.md @@ -280,8 +280,7 @@ trivy k8s --format json -o results.json cluster "Type": "kubernetes", "MisconfSummary": { "Successes": 20, - "Failures": 19, - "Exceptions": 0 + "Failures": 19 }, "Misconfigurations": [ { diff --git a/integration/repo_test.go b/integration/repo_test.go index 69833bcf2b77..6b4e3a338457 100644 --- a/integration/repo_test.go +++ b/integration/repo_test.go @@ -309,15 +309,6 @@ func TestRepository(t *testing.T) { }, golden: "testdata/dockerfile-rule-exception.json.golden", }, - { - name: "dockerfile with namespace exception", - args: args{ - scanner: types.MisconfigScanner, - policyPaths: []string{"testdata/fixtures/repo/namespace-exception/policy"}, - input: "testdata/fixtures/repo/namespace-exception", - }, - golden: "testdata/dockerfile-namespace-exception.json.golden", - }, { name: "dockerfile with custom policies", args: args{ diff --git a/integration/testdata/dockerfile-custom-policies.json.golden b/integration/testdata/dockerfile-custom-policies.json.golden index 438ecf5d7270..2cbda6f16703 100644 --- a/integration/testdata/dockerfile-custom-policies.json.golden +++ b/integration/testdata/dockerfile-custom-policies.json.golden @@ -22,8 +22,7 @@ "Type": "dockerfile", "MisconfSummary": { "Successes": 27, - "Failures": 2, - "Exceptions": 0 + "Failures": 2 }, "Misconfigurations": [ { diff --git a/integration/testdata/dockerfile-namespace-exception.json.golden b/integration/testdata/dockerfile-namespace-exception.json.golden deleted file mode 100644 index be3a6adfa8d6..000000000000 --- a/integration/testdata/dockerfile-namespace-exception.json.golden +++ /dev/null @@ -1,30 +0,0 @@ -{ - "SchemaVersion": 2, - "CreatedAt": "2021-08-25T12:20:30.000000005Z", - "ArtifactName": "testdata/fixtures/repo/namespace-exception", - "ArtifactType": "repository", - "Metadata": { - "ImageConfig": { - "architecture": "", - "created": "0001-01-01T00:00:00Z", - "os": "", - "rootfs": { - "type": "", - "diff_ids": null - }, - "config": {} - } - }, - "Results": [ - { - "Target": "Dockerfile", - "Class": "config", - "Type": "dockerfile", - "MisconfSummary": { - "Successes": 0, - "Failures": 0, - "Exceptions": 27 - } - } - ] -} diff --git a/integration/testdata/dockerfile-rule-exception.json.golden b/integration/testdata/dockerfile-rule-exception.json.golden index eae4e1e32fd6..eedcb902e145 100644 --- a/integration/testdata/dockerfile-rule-exception.json.golden +++ b/integration/testdata/dockerfile-rule-exception.json.golden @@ -22,8 +22,7 @@ "Type": "dockerfile", "MisconfSummary": { "Successes": 26, - "Failures": 1, - "Exceptions": 0 + "Failures": 1 }, "Misconfigurations": [ { diff --git a/integration/testdata/dockerfile.json.golden b/integration/testdata/dockerfile.json.golden index 3f6dd1b5b201..834b110404c1 100644 --- a/integration/testdata/dockerfile.json.golden +++ b/integration/testdata/dockerfile.json.golden @@ -22,8 +22,7 @@ "Type": "dockerfile", "MisconfSummary": { "Successes": 26, - "Failures": 1, - "Exceptions": 0 + "Failures": 1 }, "Misconfigurations": [ { diff --git a/integration/testdata/dockerfile_file_pattern.json.golden b/integration/testdata/dockerfile_file_pattern.json.golden index fe0372ddc79c..ed917023bc4b 100644 --- a/integration/testdata/dockerfile_file_pattern.json.golden +++ b/integration/testdata/dockerfile_file_pattern.json.golden @@ -22,8 +22,7 @@ "Type": "dockerfile", "MisconfSummary": { "Successes": 26, - "Failures": 1, - "Exceptions": 0 + "Failures": 1 }, "Misconfigurations": [ { diff --git a/integration/testdata/fixtures/repo/namespace-exception/Dockerfile b/integration/testdata/fixtures/repo/namespace-exception/Dockerfile deleted file mode 100644 index fa4b7580b688..000000000000 --- a/integration/testdata/fixtures/repo/namespace-exception/Dockerfile +++ /dev/null @@ -1,2 +0,0 @@ -FROM alpine:3.13 -LABEL user.root="allow" diff --git a/integration/testdata/fixtures/repo/namespace-exception/policy/exception.rego b/integration/testdata/fixtures/repo/namespace-exception/policy/exception.rego deleted file mode 100644 index 74abdccb1933..000000000000 --- a/integration/testdata/fixtures/repo/namespace-exception/policy/exception.rego +++ /dev/null @@ -1,8 +0,0 @@ -package namespace.exceptions - -import data.namespaces - -exception[ns] { - ns := data.namespaces[_] - startswith(ns, "builtin") -} \ No newline at end of file diff --git a/integration/testdata/helm.json.golden b/integration/testdata/helm.json.golden index 518458ac1088..8b9ccb11bc9c 100644 --- a/integration/testdata/helm.json.golden +++ b/integration/testdata/helm.json.golden @@ -22,8 +22,7 @@ "Type": "helm", "MisconfSummary": { "Successes": 80, - "Failures": 14, - "Exceptions": 0 + "Failures": 14 }, "Misconfigurations": [ { diff --git a/integration/testdata/helm_testchart.json.golden b/integration/testdata/helm_testchart.json.golden index 2e659b13e68a..886b0a702496 100644 --- a/integration/testdata/helm_testchart.json.golden +++ b/integration/testdata/helm_testchart.json.golden @@ -22,8 +22,7 @@ "Type": "helm", "MisconfSummary": { "Successes": 90, - "Failures": 4, - "Exceptions": 0 + "Failures": 4 }, "Misconfigurations": [ { @@ -342,8 +341,7 @@ "Type": "helm", "MisconfSummary": { "Successes": 61, - "Failures": 0, - "Exceptions": 0 + "Failures": 0 } }, { @@ -352,8 +350,7 @@ "Type": "helm", "MisconfSummary": { "Successes": 60, - "Failures": 0, - "Exceptions": 0 + "Failures": 0 } } ] diff --git a/integration/testdata/helm_testchart.overridden.json.golden b/integration/testdata/helm_testchart.overridden.json.golden index f4c984daa458..354cb3cbf786 100644 --- a/integration/testdata/helm_testchart.overridden.json.golden +++ b/integration/testdata/helm_testchart.overridden.json.golden @@ -22,8 +22,7 @@ "Type": "helm", "MisconfSummary": { "Successes": 88, - "Failures": 6, - "Exceptions": 0 + "Failures": 6 }, "Misconfigurations": [ { @@ -569,8 +568,7 @@ "Type": "helm", "MisconfSummary": { "Successes": 61, - "Failures": 0, - "Exceptions": 0 + "Failures": 0 } }, { @@ -579,8 +577,7 @@ "Type": "helm", "MisconfSummary": { "Successes": 60, - "Failures": 0, - "Exceptions": 0 + "Failures": 0 } } ] diff --git a/mkdocs.yml b/mkdocs.yml index 00e458e15a91..ab3d51b71275 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -56,7 +56,6 @@ nav: - Overview: docs/scanner/misconfiguration/index.md - Policy: - Built-in Checks: docs/scanner/misconfiguration/check/builtin.md - - Exceptions: docs/scanner/misconfiguration/check/exceptions.md - Custom Checks: - Overview: docs/scanner/misconfiguration/custom/index.md - Data: docs/scanner/misconfiguration/custom/data.md diff --git a/pkg/cache/mock_artifact_cache.go b/pkg/cache/mock_artifact_cache.go index e0b1430337e4..ef9503203a7f 100644 --- a/pkg/cache/mock_artifact_cache.go +++ b/pkg/cache/mock_artifact_cache.go @@ -229,9 +229,6 @@ func (_m *MockArtifactCache) PutBlob(blobID string, blobInfo types.BlobInfo) err for j := range blobInfo.Misconfigurations[i].Warnings { blobInfo.Misconfigurations[i].Warnings[j].Code = types.Code{} } - for j := range blobInfo.Misconfigurations[i].Exceptions { - blobInfo.Misconfigurations[i].Exceptions[j].Code = types.Code{} - } } ret := _m.Called(blobID, blobInfo) diff --git a/pkg/compliance/report/report_test.go b/pkg/compliance/report/report_test.go index e673bd50a24d..5481299b57ea 100644 --- a/pkg/compliance/report/report_test.go +++ b/pkg/compliance/report/report_test.go @@ -36,9 +36,8 @@ func TestBuildComplianceReport(t *testing.T) { Class: types.ClassConfig, Type: ftypes.Kubernetes, MisconfSummary: &types.MisconfSummary{ - Successes: 1, - Failures: 0, - Exceptions: 0, + Successes: 1, + Failures: 0, }, Misconfigurations: []types.DetectedMisconfiguration{ { @@ -158,9 +157,8 @@ func TestBuildComplianceReport(t *testing.T) { Class: types.ClassConfig, Type: ftypes.Kubernetes, MisconfSummary: &types.MisconfSummary{ - Successes: 1, - Failures: 0, - Exceptions: 0, + Successes: 1, + Failures: 0, }, Misconfigurations: []types.DetectedMisconfiguration{ { diff --git a/pkg/compliance/report/testdata/table_all.txt b/pkg/compliance/report/testdata/table_all.txt index 8a789b2ceaa5..159d5f8590ad 100644 --- a/pkg/compliance/report/testdata/table_all.txt +++ b/pkg/compliance/report/testdata/table_all.txt @@ -1,9 +1,9 @@ - -Deployment/metrics-server (kubernetes) -====================================== -Tests: 1 (SUCCESSES: 1, FAILURES: 0, EXCEPTIONS: 0) -Failures: 0 () - + +Deployment/metrics-server (kubernetes) +====================================== +Tests: 1 (SUCCESSES: 1, FAILURES: 0) +Failures: 0 () + MEDIUM: Container 'metrics-server' of Deployment 'metrics-server' should set 'securityContext.allowPrivilegeEscalation' to false ════════════════════════════════════════ A program inside the container can elevate its own privileges and run as root, which might give the program control over the container and node. @@ -24,12 +24,12 @@ See https://avd.aquasec.com/misconfig/ksv001 ──────────────────────────────────────── - -Deployment/metrics-server (kubernetes) -====================================== -Tests: 1 (SUCCESSES: 1, FAILURES: 0, EXCEPTIONS: 0) -Failures: 0 () - + +Deployment/metrics-server (kubernetes) +====================================== +Tests: 1 (SUCCESSES: 1, FAILURES: 0) +Failures: 0 () + LOW: Container 'metrics-server' of Deployment 'metrics-server' should add 'ALL' to 'securityContext.capabilities.drop' ════════════════════════════════════════ The container should drop all default capabilities and add only those that are needed for its execution. diff --git a/pkg/compliance/spec/mapper.go b/pkg/compliance/spec/mapper.go index 6fa5d2bbd45a..8c683753ad90 100644 --- a/pkg/compliance/spec/mapper.go +++ b/pkg/compliance/spec/mapper.go @@ -49,8 +49,6 @@ func misconfigSummary(misconfig types.DetectedMisconfiguration) *types.MisconfSu rms.Successes = 1 case types.MisconfStatusFailure: rms.Failures = 1 - case types.MisconfStatusException: - rms.Exceptions = 1 } return &rms } diff --git a/pkg/compliance/spec/mapper_test.go b/pkg/compliance/spec/mapper_test.go index 62050d27ec47..070c06a8ce96 100644 --- a/pkg/compliance/spec/mapper_test.go +++ b/pkg/compliance/spec/mapper_test.go @@ -61,9 +61,8 @@ func TestMapSpecCheckIDToFilteredResults(t *testing.T) { Class: types.ClassConfig, Type: ftypes.Kubernetes, MisconfSummary: &types.MisconfSummary{ - Successes: 0, - Failures: 1, - Exceptions: 0, + Successes: 0, + Failures: 1, }, Misconfigurations: []types.DetectedMisconfiguration{ { @@ -79,9 +78,8 @@ func TestMapSpecCheckIDToFilteredResults(t *testing.T) { Class: types.ClassConfig, Type: ftypes.Kubernetes, MisconfSummary: &types.MisconfSummary{ - Successes: 0, - Failures: 1, - Exceptions: 0, + Successes: 0, + Failures: 1, }, Misconfigurations: []types.DetectedMisconfiguration{ { diff --git a/pkg/fanal/types/misconf.go b/pkg/fanal/types/misconf.go index 3b4e9b447895..706220f0a1c7 100644 --- a/pkg/fanal/types/misconf.go +++ b/pkg/fanal/types/misconf.go @@ -8,13 +8,12 @@ import ( ) type Misconfiguration struct { - FileType ConfigType `json:",omitempty"` - FilePath string `json:",omitempty"` - Successes MisconfResults `json:",omitempty"` - Warnings MisconfResults `json:",omitempty"` - Failures MisconfResults `json:",omitempty"` - Exceptions MisconfResults `json:",omitempty"` - Layer Layer `json:",omitempty"` + FileType ConfigType `json:",omitempty"` + FilePath string `json:",omitempty"` + Successes MisconfResults `json:",omitempty"` + Warnings MisconfResults `json:",omitempty"` + Failures MisconfResults `json:",omitempty"` + Layer Layer `json:",omitempty"` } type MisconfResult struct { @@ -117,7 +116,6 @@ func ToMisconfigurations(misconfs map[string]Misconfiguration) []Misconfiguratio sort.Sort(misconf.Successes) sort.Sort(misconf.Warnings) sort.Sort(misconf.Failures) - sort.Sort(misconf.Exceptions) results = append(results, misconf) } diff --git a/pkg/flag/misconf_flags.go b/pkg/flag/misconf_flags.go index 128ecfcac42e..421e9c899285 100644 --- a/pkg/flag/misconf_flags.go +++ b/pkg/flag/misconf_flags.go @@ -34,7 +34,7 @@ var ( IncludeNonFailuresFlag = Flag[bool]{ Name: "include-non-failures", ConfigName: "misconfiguration.include-non-failures", - Usage: "include successes and exceptions, available with '--scanners misconfig'", + Usage: "include successes, available with '--scanners misconfig'", } HelmValuesFileFlag = Flag[[]string]{ Name: "helm-values", diff --git a/pkg/iac/rego/exceptions.go b/pkg/iac/rego/exceptions.go deleted file mode 100644 index 591c72abdbbb..000000000000 --- a/pkg/iac/rego/exceptions.go +++ /dev/null @@ -1,35 +0,0 @@ -package rego - -import ( - "context" - "fmt" - - "github.com/open-policy-agent/opa/ast" -) - -func (s *Scanner) isIgnored(ctx context.Context, namespace, ruleName string, input ast.Value) (bool, error) { - if ignored, err := s.isNamespaceIgnored(ctx, namespace, input); err != nil { - return false, err - } else if ignored { - return true, nil - } - return s.isRuleIgnored(ctx, namespace, ruleName, input) -} - -func (s *Scanner) isNamespaceIgnored(ctx context.Context, namespace string, input ast.Value) (bool, error) { - exceptionQuery := fmt.Sprintf("data.namespace.exceptions.exception[_] == %q", namespace) - result, _, err := s.runQuery(ctx, exceptionQuery, input, true) - if err != nil { - return false, fmt.Errorf("query namespace exceptions: %w", err) - } - return result.Allowed(), nil -} - -func (s *Scanner) isRuleIgnored(ctx context.Context, namespace, ruleName string, input ast.Value) (bool, error) { - exceptionQuery := fmt.Sprintf("endswith(%q, data.%s.exception[_][_])", ruleName, namespace) - result, _, err := s.runQuery(ctx, exceptionQuery, input, true) - if err != nil { - return false, err - } - return result.Allowed(), nil -} diff --git a/pkg/iac/rego/scanner.go b/pkg/iac/rego/scanner.go index 9cf80fc83da0..f791a45bf74d 100644 --- a/pkg/iac/rego/scanner.go +++ b/pkg/iac/rego/scanner.go @@ -345,16 +345,7 @@ func (s *Scanner) applyRule(ctx context.Context, namespace, rule string, inputs s.logger.Error("Error occurred while parsing input", log.Err(err)) continue } - if ignored, err := s.isIgnored(ctx, namespace, rule, parsedInput); err != nil { - return nil, err - } else if ignored { - var result regoResult - result.FS = input.FS - result.Filepath = input.Path - result.Managed = true - results.AddIgnored(result) - continue - } + set, traces, err := s.runQuery(ctx, qualified, parsedInput, false) if err != nil { return nil, err @@ -385,20 +376,6 @@ func (s *Scanner) applyRuleCombined(ctx context.Context, namespace, rule string, return nil, fmt.Errorf("failed to parse input: %w", err) } - var results scan.Results - - if ignored, err := s.isIgnored(ctx, namespace, rule, parsed); err != nil { - return nil, err - } else if ignored { - for _, input := range inputs { - var result regoResult - result.FS = input.FS - result.Filepath = input.Path - result.Managed = true - results.AddIgnored(result) - } - return results, nil - } qualified := fmt.Sprintf("data.%s.%s", namespace, rule) set, traces, err := s.runQuery(ctx, qualified, parsed, false) if err != nil { diff --git a/pkg/iac/rego/scanner_test.go b/pkg/iac/rego/scanner_test.go index 1310e56d2ec4..880bc9c1f9f9 100644 --- a/pkg/iac/rego/scanner_test.go +++ b/pkg/iac/rego/scanner_test.go @@ -166,168 +166,6 @@ deny { assert.Equal(t, "/evil.lol", results.GetPassed()[0].Metadata().Range().GetFilename()) } -func Test_RegoScanning_Namespace_Exception(t *testing.T) { - - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test - -deny { - input.evil -} -`, - "policies/exceptions.rego": ` -package namespace.exceptions - -import data.namespaces - -exception[ns] { - ns := data.namespaces[_] - startswith(ns, "defsec") -} -`, - }) - - scanner := rego.NewScanner( - types.SourceJSON, - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": true, - }, - }) - require.NoError(t, err) - - assert.Empty(t, results.GetFailed()) - assert.Empty(t, results.GetPassed()) - assert.Len(t, results.GetIgnored(), 1) - -} - -func Test_RegoScanning_Namespace_Exception_WithoutMatch(t *testing.T) { - - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test - -deny { - input.evil -} -`, "policies/something.rego": ` -package builtin.test - -deny_something { - input.something -} -`, - "policies/exceptions.rego": ` -package namespace.exceptions - -import data.namespaces - -exception[ns] { - ns := data.namespaces[_] - startswith(ns, "builtin") -} -`, - }) - - scanner := rego.NewScanner( - types.SourceJSON, - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": true, - }, - }) - require.NoError(t, err) - - assert.Len(t, results.GetFailed(), 1) - assert.Empty(t, results.GetPassed()) - assert.Len(t, results.GetIgnored(), 1) - -} - -func Test_RegoScanning_Rule_Exception(t *testing.T) { - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test -deny_evil { - input.evil -} -`, - "policies/exceptions.rego": ` -package defsec.test - -exception[rules] { - rules := ["evil"] -} -`, - }) - - scanner := rego.NewScanner( - types.SourceJSON, - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": true, - }, - }) - require.NoError(t, err) - - assert.Empty(t, results.GetFailed()) - assert.Empty(t, results.GetPassed()) - assert.Len(t, results.GetIgnored(), 1) -} - -func Test_RegoScanning_Rule_Exception_WithoutMatch(t *testing.T) { - srcFS := CreateFS(t, map[string]string{ - "policies/test.rego": ` -package defsec.test -deny_evil { - input.evil -} -`, - "policies/exceptions.rego": ` -package defsec.test - -exception[rules] { - rules := ["good"] -} -`, - }) - - scanner := rego.NewScanner( - types.SourceJSON, - rego.WithPolicyDirs("policies"), - ) - require.NoError(t, scanner.LoadPolicies(srcFS)) - - results, err := scanner.ScanInput(context.TODO(), rego.Input{ - Path: "/evil.lol", - Contents: map[string]any{ - "evil": true, - }, - }) - require.NoError(t, err) - - assert.Len(t, results.GetFailed(), 1) - assert.Empty(t, results.GetPassed()) - assert.Empty(t, results.GetIgnored()) -} - func Test_RegoScanning_WithRuntimeValues(t *testing.T) { t.Setenv("DEFSEC_RUNTIME_VAL", "AOK") diff --git a/pkg/iac/scanners/cloudformation/scanner.go b/pkg/iac/scanners/cloudformation/scanner.go index 4497a807506c..0e3fec472b96 100644 --- a/pkg/iac/scanners/cloudformation/scanner.go +++ b/pkg/iac/scanners/cloudformation/scanner.go @@ -205,6 +205,7 @@ func (s *Scanner) scanFileContext(ctx context.Context, regoScanner *rego.Scanner } results = append(results, regoResults...) + // ignore a result based on user input results.Ignore(cfCtx.Ignores, nil) for _, ignored := range results.GetIgnored() { diff --git a/pkg/iac/scanners/terraform/executor/executor.go b/pkg/iac/scanners/terraform/executor/executor.go index 452e47dcc8de..8e14f778e5b4 100644 --- a/pkg/iac/scanners/terraform/executor/executor.go +++ b/pkg/iac/scanners/terraform/executor/executor.go @@ -85,6 +85,7 @@ func (e *Executor) Execute(modules terraform.Modules) (scan.Results, error) { "ignore": attributeIgnorer(modules), } + // ignore a result based on user input results.Ignore(ignores, ignorers) for _, ignored := range results.GetIgnored() { diff --git a/pkg/misconf/scanner.go b/pkg/misconf/scanner.go index 7a3642dfe895..1aa2a5cd5c16 100644 --- a/pkg/misconf/scanner.go +++ b/pkg/misconf/scanner.go @@ -488,8 +488,6 @@ func ResultsToMisconf(configType types.ConfigType, scannerName string, results s switch flattened.Status { case scan.StatusPassed: misconf.Successes = append(misconf.Successes, misconfResult) - case scan.StatusIgnored: - misconf.Exceptions = append(misconf.Exceptions, misconfResult) case scan.StatusFailed: misconf.Failures = append(misconf.Failures, misconfResult) } diff --git a/pkg/report/table/misconfig.go b/pkg/report/table/misconfig.go index dfb724bdf458..41f111d883c9 100644 --- a/pkg/report/table/misconfig.go +++ b/pkg/report/table/misconfig.go @@ -61,8 +61,8 @@ func (r *misconfigRenderer) Render() string { total, summaries := summarize(r.severities, r.countSeverities()) summary := r.result.MisconfSummary - r.printf("Tests: %d (SUCCESSES: %d, FAILURES: %d, EXCEPTIONS: %d)\n", - summary.Successes+summary.Failures+summary.Exceptions, summary.Successes, summary.Failures, summary.Exceptions) + r.printf("Tests: %d (SUCCESSES: %d, FAILURES: %d)\n", + summary.Successes+summary.Failures, summary.Successes, summary.Failures) r.printf("Failures: %d (%s)\n\n", total, strings.Join(summaries, ", ")) for _, m := range r.result.Misconfigurations { diff --git a/pkg/report/table/misconfig_test.go b/pkg/report/table/misconfig_test.go index 785b2621b504..8d17798f673d 100644 --- a/pkg/report/table/misconfig_test.go +++ b/pkg/report/table/misconfig_test.go @@ -24,7 +24,7 @@ func TestMisconfigRenderer(t *testing.T) { name: "single result", input: types.Result{ Target: "my-file", - MisconfSummary: &types.MisconfSummary{Successes: 0, Failures: 1, Exceptions: 0}, + MisconfSummary: &types.MisconfSummary{Successes: 0, Failures: 1}, Misconfigurations: []types.DetectedMisconfiguration{ { ID: "some-alias-for-a-check", @@ -42,7 +42,7 @@ func TestMisconfigRenderer(t *testing.T) { want: ` my-file () ========== -Tests: 1 (SUCCESSES: 0, FAILURES: 1, EXCEPTIONS: 0) +Tests: 1 (SUCCESSES: 0, FAILURES: 1) Failures: 1 (LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0) AVD-XYZ-0123 (HIGH): Oh no, a bad config. @@ -59,7 +59,7 @@ See https://google.com/search?q=bad%20config name: "single result with code", input: types.Result{ Target: "my-file", - MisconfSummary: &types.MisconfSummary{Successes: 0, Failures: 1, Exceptions: 0}, + MisconfSummary: &types.MisconfSummary{Successes: 0, Failures: 1}, Misconfigurations: []types.DetectedMisconfiguration{ { AVDID: "AVD-XYZ-0123", @@ -101,7 +101,7 @@ See https://google.com/search?q=bad%20config want: ` my-file () ========== -Tests: 1 (SUCCESSES: 0, FAILURES: 1, EXCEPTIONS: 0) +Tests: 1 (SUCCESSES: 0, FAILURES: 1) Failures: 1 (LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0) AVD-XYZ-0123 (HIGH): Oh no, a bad config. @@ -124,7 +124,7 @@ See https://google.com/search?q=bad%20config name: "multiple results", input: types.Result{ Target: "my-file", - MisconfSummary: &types.MisconfSummary{Successes: 1, Failures: 1, Exceptions: 0}, + MisconfSummary: &types.MisconfSummary{Successes: 1, Failures: 1}, Misconfigurations: []types.DetectedMisconfiguration{ { AVDID: "AVD-XYZ-0123", @@ -172,7 +172,7 @@ See https://google.com/search?q=bad%20config want: ` my-file () ========== -Tests: 2 (SUCCESSES: 1, FAILURES: 1, EXCEPTIONS: 0) +Tests: 2 (SUCCESSES: 1, FAILURES: 1) Failures: 1 (LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0) FAIL: AVD-XYZ-0123 (HIGH): Oh no, a bad config. @@ -206,9 +206,8 @@ See https://google.com/search?q=bad%20config Class: types.ClassConfig, Type: "terraform", MisconfSummary: &types.MisconfSummary{ - Successes: 5, - Failures: 1, - Exceptions: 0, + Successes: 5, + Failures: 1, }, Misconfigurations: []types.DetectedMisconfiguration{ { @@ -310,7 +309,7 @@ See https://google.com/search?q=bad%20config want: ` terraform-aws-modules/security-group/aws/main.tf (terraform) ============================================================ -Tests: 6 (SUCCESSES: 5, FAILURES: 1, EXCEPTIONS: 0) +Tests: 6 (SUCCESSES: 5, FAILURES: 1) Failures: 1 (LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 1) AVD-AWS-0107 (CRITICAL): Security group rule allows ingress from public internet. diff --git a/pkg/result/filter.go b/pkg/result/filter.go index e1f0e632197e..83e0c0a7170e 100644 --- a/pkg/result/filter.go +++ b/pkg/result/filter.go @@ -130,13 +130,12 @@ func filterMisconfigurations(result *types.Result, severities []string, includeN // Filter by ignore file if f := ignoreConfig.MatchMisconfiguration(misconf.ID, misconf.AVDID, result.Target); f != nil { - result.MisconfSummary.Exceptions++ result.ModifiedFindings = append(result.ModifiedFindings, types.NewModifiedFinding(misconf, types.FindingStatusIgnored, f.Statement, ignoreConfig.FilePath)) continue } - // Count successes, failures, and exceptions + // Count successes and failures summarize(misconf.Status, result.MisconfSummary) if misconf.Status != types.MisconfStatusFailure && !includeNonFailures { @@ -210,8 +209,6 @@ func summarize(status types.MisconfStatus, summary *types.MisconfSummary) { summary.Failures++ case types.MisconfStatusPassed: summary.Successes++ - case types.MisconfStatusException: - summary.Exceptions++ } } @@ -256,7 +253,6 @@ func applyPolicy(ctx context.Context, result *types.Result, policyFile string) e return err } if ignored { - result.MisconfSummary.Exceptions++ switch misconf.Status { case types.MisconfStatusFailure: result.MisconfSummary.Failures-- diff --git a/pkg/result/filter_test.go b/pkg/result/filter_test.go index 0298cd0d9582..289ec2ee0c63 100644 --- a/pkg/result/filter_test.go +++ b/pkg/result/filter_test.go @@ -233,9 +233,8 @@ func TestFilter(t *testing.T) { vuln2, }, MisconfSummary: &types.MisconfSummary{ - Successes: 0, - Failures: 1, - Exceptions: 0, + Successes: 0, + Failures: 1, }, Misconfigurations: []types.DetectedMisconfiguration{ misconf1, @@ -403,9 +402,8 @@ func TestFilter(t *testing.T) { Target: "deployment.yaml", Class: types.ClassConfig, MisconfSummary: &types.MisconfSummary{ - Successes: 1, - Failures: 1, - Exceptions: 1, + Successes: 1, + Failures: 1, }, Misconfigurations: []types.DetectedMisconfiguration{ misconf1, @@ -522,9 +520,8 @@ func TestFilter(t *testing.T) { { Target: "app/Dockerfile", MisconfSummary: &types.MisconfSummary{ - Successes: 0, - Failures: 1, - Exceptions: 2, + Successes: 0, + Failures: 1, }, Misconfigurations: []types.DetectedMisconfiguration{ misconf3, @@ -641,9 +638,8 @@ func TestFilter(t *testing.T) { Results: types.Results{ { MisconfSummary: &types.MisconfSummary{ - Successes: 1, - Failures: 1, - Exceptions: 1, + Successes: 1, + Failures: 1, }, Misconfigurations: []types.DetectedMisconfiguration{ misconf1, diff --git a/pkg/rpc/convert.go b/pkg/rpc/convert.go index 1662877bd597..89097730111b 100644 --- a/pkg/rpc/convert.go +++ b/pkg/rpc/convert.go @@ -754,13 +754,12 @@ func ConvertFromRPCMisconfigurations(rpcMisconfs []*common.Misconfiguration) []f var misconfs []ftypes.Misconfiguration for _, rpcMisconf := range rpcMisconfs { misconfs = append(misconfs, ftypes.Misconfiguration{ - FileType: ftypes.ConfigType(rpcMisconf.FileType), - FilePath: rpcMisconf.FilePath, - Successes: ConvertFromRPCMisconfResults(rpcMisconf.Successes), - Warnings: ConvertFromRPCMisconfResults(rpcMisconf.Warnings), - Failures: ConvertFromRPCMisconfResults(rpcMisconf.Failures), - Exceptions: ConvertFromRPCMisconfResults(rpcMisconf.Exceptions), - Layer: ftypes.Layer{}, + FileType: ftypes.ConfigType(rpcMisconf.FileType), + FilePath: rpcMisconf.FilePath, + Successes: ConvertFromRPCMisconfResults(rpcMisconf.Successes), + Warnings: ConvertFromRPCMisconfResults(rpcMisconf.Warnings), + Failures: ConvertFromRPCMisconfResults(rpcMisconf.Failures), + Layer: ftypes.Layer{}, }) } return misconfs @@ -875,12 +874,11 @@ func ConvertToRPCPutBlobRequest(diffID string, blobInfo ftypes.BlobInfo) *cache. var misconfigurations []*common.Misconfiguration for _, m := range blobInfo.Misconfigurations { misconfigurations = append(misconfigurations, &common.Misconfiguration{ - FileType: string(m.FileType), - FilePath: m.FilePath, - Successes: ConvertToMisconfResults(m.Successes), - Warnings: ConvertToMisconfResults(m.Warnings), - Failures: ConvertToMisconfResults(m.Failures), - Exceptions: ConvertToMisconfResults(m.Exceptions), + FileType: string(m.FileType), + FilePath: m.FilePath, + Successes: ConvertToMisconfResults(m.Successes), + Warnings: ConvertToMisconfResults(m.Warnings), + Failures: ConvertToMisconfResults(m.Failures), }) } diff --git a/pkg/scanner/local/scan.go b/pkg/scanner/local/scan.go index f34723058e06..58cd4cc00167 100644 --- a/pkg/scanner/local/scan.go +++ b/pkg/scanner/local/scan.go @@ -210,9 +210,6 @@ func (s Scanner) MisconfsToResults(misconfs []ftypes.Misconfiguration) types.Res for _, w := range misconf.Successes { detected = append(detected, toDetectedMisconfiguration(w, dbTypes.SeverityUnknown, types.MisconfStatusPassed, misconf.Layer)) } - for _, w := range misconf.Exceptions { - detected = append(detected, toDetectedMisconfiguration(w, dbTypes.SeverityUnknown, types.MisconfStatusException, misconf.Layer)) - } results = append(results, types.Result{ Target: misconf.FilePath, diff --git a/pkg/scanner/local/scan_test.go b/pkg/scanner/local/scan_test.go index f3e6cac5d3fa..bdeb5166a2fa 100644 --- a/pkg/scanner/local/scan_test.go +++ b/pkg/scanner/local/scan_test.go @@ -820,17 +820,6 @@ func TestScanner_Scan(t *testing.T) { }, }, }, - Exceptions: ftypes.MisconfResults{ - { - Namespace: "main.kubernetes.id100", - PolicyMetadata: ftypes.PolicyMetadata{ - ID: "ID100", - Type: "Kubernetes Security Check", - Title: "Bad Deployment", - Severity: "HIGH", - }, - }, - }, Layer: ftypes.Layer{ DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303", }, @@ -922,18 +911,6 @@ func TestScanner_Scan(t *testing.T) { DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303", }, }, - { - Type: "Kubernetes Security Check", - ID: "ID100", - Title: "Bad Deployment", - Message: "No issues found", - Namespace: "main.kubernetes.id100", - Severity: "HIGH", - Status: types.MisconfStatusException, - Layer: ftypes.Layer{ - DiffID: "sha256:9922bc15eeefe1637b803ef2106f178152ce19a391f24aec838cbe2e48e73303", - }, - }, }, }, }, diff --git a/pkg/types/report.go b/pkg/types/report.go index af364c95a11d..3ada68a942d8 100644 --- a/pkg/types/report.go +++ b/pkg/types/report.go @@ -130,13 +130,12 @@ func (r *Result) IsEmpty() bool { } type MisconfSummary struct { - Successes int - Failures int - Exceptions int + Successes int + Failures int } func (s MisconfSummary) Empty() bool { - return s.Successes == 0 && s.Failures == 0 && s.Exceptions == 0 + return s.Successes == 0 && s.Failures == 0 } // Failed returns whether the result includes any vulnerabilities, misconfigurations or secrets diff --git a/rpc/cache/service.pb.go b/rpc/cache/service.pb.go index d3a1fa1a63b9..acca9623e93a 100644 --- a/rpc/cache/service.pb.go +++ b/rpc/cache/service.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 +// protoc-gen-go v1.34.0 // protoc v3.19.4 // source: rpc/cache/service.proto diff --git a/rpc/common/service.pb.go b/rpc/common/service.pb.go index 4c7f0af79464..c8290cc52818 100644 --- a/rpc/common/service.pb.go +++ b/rpc/common/service.pb.go @@ -755,12 +755,11 @@ type Misconfiguration struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - FileType string `protobuf:"bytes,1,opt,name=file_type,json=fileType,proto3" json:"file_type,omitempty"` - FilePath string `protobuf:"bytes,2,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"` - Successes []*MisconfResult `protobuf:"bytes,3,rep,name=successes,proto3" json:"successes,omitempty"` - Warnings []*MisconfResult `protobuf:"bytes,4,rep,name=warnings,proto3" json:"warnings,omitempty"` - Failures []*MisconfResult `protobuf:"bytes,5,rep,name=failures,proto3" json:"failures,omitempty"` - Exceptions []*MisconfResult `protobuf:"bytes,6,rep,name=exceptions,proto3" json:"exceptions,omitempty"` + FileType string `protobuf:"bytes,1,opt,name=file_type,json=fileType,proto3" json:"file_type,omitempty"` + FilePath string `protobuf:"bytes,2,opt,name=file_path,json=filePath,proto3" json:"file_path,omitempty"` + Successes []*MisconfResult `protobuf:"bytes,3,rep,name=successes,proto3" json:"successes,omitempty"` + Warnings []*MisconfResult `protobuf:"bytes,4,rep,name=warnings,proto3" json:"warnings,omitempty"` + Failures []*MisconfResult `protobuf:"bytes,5,rep,name=failures,proto3" json:"failures,omitempty"` } func (x *Misconfiguration) Reset() { @@ -830,13 +829,6 @@ func (x *Misconfiguration) GetFailures() []*MisconfResult { return nil } -func (x *Misconfiguration) GetExceptions() []*MisconfResult { - if x != nil { - return x.Exceptions - } - return nil -} - type MisconfResult struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2482,7 +2474,7 @@ var file_rpc_common_service_proto_rawDesc = []byte{ 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x65, 0x6e, - 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x22, 0xb6, 0x02, 0x0a, 0x10, 0x4d, 0x69, 0x73, 0x63, 0x6f, 0x6e, + 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x22, 0xff, 0x01, 0x0a, 0x10, 0x4d, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, @@ -2498,300 +2490,296 @@ var file_rpc_common_service_proto_rawDesc = []byte{ 0x75, 0x72, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x66, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x52, 0x08, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, - 0x73, 0x12, 0x3b, 0x0a, 0x0a, 0x65, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x66, 0x52, 0x65, 0x73, 0x75, - 0x6c, 0x74, 0x52, 0x0a, 0x65, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xf3, - 0x01, 0x0a, 0x0d, 0x4d, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x66, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x18, - 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x45, 0x0a, 0x0f, 0x70, 0x6f, 0x6c, 0x69, - 0x63, 0x79, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, - 0x0e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, - 0x42, 0x0a, 0x0e, 0x63, 0x61, 0x75, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, - 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x75, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x63, 0x61, 0x75, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x07, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x52, - 0x02, 0x69, 0x64, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, - 0x72, 0x69, 0x74, 0x79, 0x22, 0xf0, 0x01, 0x0a, 0x0e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x64, 0x76, 0x5f, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x64, 0x76, 0x49, 0x64, 0x12, 0x12, - 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, - 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, - 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x2f, 0x0a, 0x13, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, - 0x65, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x12, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x65, 0x64, - 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, - 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x66, - 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x22, 0xf7, 0x03, 0x0a, 0x18, 0x44, 0x65, 0x74, 0x65, - 0x63, 0x74, 0x65, 0x64, 0x4d, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, - 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, - 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, - 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x73, 0x6f, - 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, - 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, - 0x72, 0x69, 0x74, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x74, 0x72, 0x69, - 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, - 0x74, 0x79, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x1f, 0x0a, 0x0b, - 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x55, 0x72, 0x6c, 0x12, 0x1e, 0x0a, - 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x16, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x0c, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, - 0x12, 0x42, 0x0a, 0x0e, 0x63, 0x61, 0x75, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x75, 0x73, 0x65, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x63, 0x61, 0x75, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x12, 0x15, 0x0a, 0x06, 0x61, 0x76, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x0e, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, 0x76, 0x64, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, - 0x79, 0x22, 0xff, 0x09, 0x0a, 0x0d, 0x56, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, 0x6c, - 0x69, 0x74, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x76, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, - 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x76, - 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x19, - 0x0a, 0x08, 0x70, 0x6b, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x70, 0x6b, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x73, - 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x56, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x66, - 0x69, 0x78, 0x65, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x74, - 0x69, 0x74, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, + 0x73, 0x4a, 0x04, 0x08, 0x06, 0x10, 0x07, 0x22, 0xf3, 0x01, 0x0a, 0x0d, 0x4d, 0x69, 0x73, 0x63, + 0x6f, 0x6e, 0x66, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, + 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, + 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x45, 0x0a, 0x0f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5f, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x72, 0x69, + 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x42, 0x0a, 0x0e, 0x63, 0x61, 0x75, 0x73, + 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x43, 0x61, 0x75, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, 0x63, + 0x61, 0x75, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x03, + 0x10, 0x07, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x52, 0x02, 0x69, 0x64, 0x52, 0x05, 0x74, 0x69, + 0x74, 0x6c, 0x65, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x22, 0xf0, 0x01, + 0x0a, 0x0e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x15, 0x0a, 0x06, 0x61, 0x64, 0x76, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x61, 0x64, 0x76, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, + 0x69, 0x74, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, - 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x52, 0x08, 0x73, - 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, - 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x66, - 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x0e, 0x70, 0x6b, 0x67, 0x5f, 0x69, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, - 0x6b, 0x67, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0d, 0x70, 0x6b, - 0x67, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x05, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72, 0x69, - 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x52, - 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, - 0x74, 0x79, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0e, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, - 0x39, 0x0a, 0x04, 0x63, 0x76, 0x73, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, - 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x56, 0x75, 0x6c, - 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x43, 0x76, 0x73, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x63, 0x76, 0x73, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x77, - 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x63, 0x77, 0x65, - 0x49, 0x64, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x75, - 0x72, 0x6c, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, - 0x79, 0x55, 0x72, 0x6c, 0x12, 0x41, 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, - 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, + 0x2f, 0x0a, 0x13, 0x72, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x72, 0x65, + 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x08, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, + 0x22, 0xf7, 0x03, 0x0a, 0x18, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4d, 0x69, 0x73, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, + 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x32, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x52, 0x08, 0x73, 0x65, 0x76, + 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, + 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6d, + 0x61, 0x72, 0x79, 0x55, 0x72, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x66, 0x65, + 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x29, + 0x0a, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x61, 0x79, + 0x65, 0x72, 0x52, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x0e, 0x63, 0x61, 0x75, + 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, + 0x2e, 0x43, 0x61, 0x75, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0d, + 0x63, 0x61, 0x75, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x15, 0x0a, + 0x06, 0x61, 0x76, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x61, + 0x76, 0x64, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x0f, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0xff, 0x09, 0x0a, 0x0d, 0x56, + 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x29, 0x0a, 0x10, + 0x76, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x76, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, + 0x69, 0x6c, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6b, 0x67, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6b, 0x67, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x2b, 0x0a, 0x11, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x5f, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, + 0x23, 0x0a, 0x0d, 0x66, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x66, 0x69, 0x78, 0x65, 0x64, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x32, 0x0a, 0x08, + 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, + 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, + 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, + 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x08, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, + 0x12, 0x42, 0x0a, 0x0e, 0x70, 0x6b, 0x67, 0x5f, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, + 0x65, 0x72, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x50, 0x6b, 0x67, 0x49, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x66, 0x69, 0x65, 0x72, 0x52, 0x0d, 0x70, 0x6b, 0x67, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x6e, 0x2e, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, + 0x27, 0x0a, 0x0f, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, + 0x74, 0x79, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x39, 0x0a, 0x04, 0x63, 0x76, 0x73, 0x73, + 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, + 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x56, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, 0x6c, + 0x69, 0x74, 0x79, 0x2e, 0x43, 0x76, 0x73, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x63, + 0x76, 0x73, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x63, 0x77, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x0d, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x63, 0x77, 0x65, 0x49, 0x64, 0x73, 0x12, 0x1f, 0x0a, 0x0b, + 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x0e, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x55, 0x72, 0x6c, 0x12, 0x41, 0x0a, + 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, + 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x52, 0x0d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x44, 0x61, 0x74, 0x65, + 0x12, 0x48, 0x0a, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, + 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, - 0x68, 0x65, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, 0x48, 0x0a, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x5f, - 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x64, 0x61, 0x74, 0x65, 0x18, 0x10, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, - 0x10, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x44, 0x61, 0x74, - 0x65, 0x12, 0x48, 0x0a, 0x14, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x61, 0x64, 0x76, 0x69, - 0x73, 0x6f, 0x72, 0x79, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x41, - 0x64, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x79, 0x44, 0x61, 0x74, 0x61, 0x12, 0x40, 0x0a, 0x10, 0x63, - 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x76, 0x75, 0x6c, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, - 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x63, - 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x56, 0x75, 0x6c, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, - 0x0a, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x09, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x49, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x0b, - 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x2e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0a, 0x64, 0x61, 0x74, - 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x58, 0x0a, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, - 0x72, 0x5f, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x2f, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, - 0x56, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x56, 0x65, - 0x6e, 0x64, 0x6f, 0x72, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x0e, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, - 0x79, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6b, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x16, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6b, 0x67, 0x50, 0x61, 0x74, 0x68, 0x12, 0x15, 0x0a, 0x06, - 0x70, 0x6b, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x17, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x6b, - 0x67, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x18, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x1a, 0x4b, 0x0a, 0x09, 0x43, - 0x76, 0x73, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x28, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x69, 0x76, - 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x56, 0x53, 0x53, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x59, 0x0a, 0x13, 0x56, 0x65, 0x6e, 0x64, - 0x6f, 0x72, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x16, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, - 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0x42, 0x0a, 0x0a, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x57, 0x0a, 0x05, 0x4c, 0x61, 0x79, 0x65, 0x72, - 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x69, 0x66, 0x66, - 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x69, 0x66, 0x66, 0x49, - 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, - 0x22, 0xc3, 0x01, 0x0a, 0x0d, 0x43, 0x61, 0x75, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1a, - 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6c, 0x69, - 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, - 0x69, 0x6e, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x26, - 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, - 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x64, 0x65, - 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, 0x76, 0x0a, 0x04, 0x43, 0x56, 0x53, 0x53, 0x12, 0x1b, - 0x0a, 0x09, 0x76, 0x32, 0x5f, 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x76, 0x32, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x76, - 0x33, 0x5f, 0x76, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x76, 0x33, 0x56, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x19, 0x0a, 0x08, 0x76, 0x32, 0x5f, 0x73, - 0x63, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x76, 0x32, 0x53, 0x63, - 0x6f, 0x72, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x76, 0x33, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x76, 0x33, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x22, 0x98, - 0x01, 0x0a, 0x0e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, - 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, - 0x74, 0x68, 0x12, 0x29, 0x0a, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x2e, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x2a, 0x0a, - 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xf3, 0x01, 0x0a, 0x04, 0x4c, 0x69, - 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, - 0x74, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x63, 0x61, 0x75, 0x73, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x43, 0x61, 0x75, 0x73, 0x65, 0x12, - 0x1e, 0x0a, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x1c, 0x0a, 0x09, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x09, 0x74, 0x72, 0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x20, 0x0a, - 0x0b, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x65, 0x64, 0x12, - 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66, 0x69, 0x72, 0x73, 0x74, 0x43, 0x61, 0x75, 0x73, 0x65, - 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x08, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x43, 0x61, 0x75, 0x73, 0x65, 0x22, - 0x30, 0x0a, 0x04, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x28, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x6e, 0x65, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, - 0x73, 0x22, 0x9f, 0x02, 0x0a, 0x0d, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x46, 0x69, 0x6e, 0x64, - 0x69, 0x6e, 0x67, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x75, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, - 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, - 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, - 0x72, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, - 0x72, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, - 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, - 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x65, 0x6e, 0x64, - 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x10, 0x6c, 0x61, 0x73, 0x74, 0x4d, 0x6f, + 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x44, 0x61, 0x74, 0x65, 0x12, 0x48, 0x0a, 0x14, 0x63, 0x75, + 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x61, 0x64, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x79, 0x5f, 0x64, 0x61, + 0x74, 0x61, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x52, 0x12, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x41, 0x64, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x79, + 0x44, 0x61, 0x74, 0x61, 0x12, 0x40, 0x0a, 0x10, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x76, + 0x75, 0x6c, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x56, 0x75, + 0x6c, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, + 0x5f, 0x69, 0x64, 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x76, 0x65, 0x6e, 0x64, + 0x6f, 0x72, 0x49, 0x64, 0x73, 0x12, 0x39, 0x0a, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x72, 0x69, + 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x52, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x12, 0x58, 0x0a, 0x0f, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x5f, 0x73, 0x65, 0x76, 0x65, 0x72, + 0x69, 0x74, 0x79, 0x18, 0x15, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x74, 0x72, 0x69, 0x76, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x56, 0x75, 0x6c, 0x6e, 0x65, 0x72, 0x61, + 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x2e, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x53, 0x65, 0x76, + 0x65, 0x72, 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x76, 0x65, 0x6e, 0x64, + 0x6f, 0x72, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6b, + 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x16, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6b, + 0x67, 0x50, 0x61, 0x74, 0x68, 0x12, 0x15, 0x0a, 0x06, 0x70, 0x6b, 0x67, 0x5f, 0x69, 0x64, 0x18, + 0x17, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x70, 0x6b, 0x67, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x18, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x1a, 0x4b, 0x0a, 0x09, 0x43, 0x76, 0x73, 0x73, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x6b, 0x65, 0x79, 0x12, 0x28, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, - 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x61, 0x74, - 0x63, 0x68, 0x12, 0x29, 0x0a, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, - 0x2e, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4a, 0x04, 0x08, - 0x09, 0x10, 0x0a, 0x22, 0x5d, 0x0a, 0x06, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, 0x74, 0x68, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x69, 0x6e, - 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, - 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x64, 0x69, 0x6e, - 0x67, 0x73, 0x22, 0x99, 0x02, 0x0a, 0x0f, 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4c, - 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x12, 0x32, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, - 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, - 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x3e, 0x0a, 0x08, 0x63, 0x61, - 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x74, + 0x6e, 0x2e, 0x43, 0x56, 0x53, 0x53, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x1a, 0x59, 0x0a, 0x13, 0x56, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x53, 0x65, 0x76, 0x65, 0x72, + 0x69, 0x74, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2c, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x74, 0x72, 0x69, 0x76, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, + 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x42, 0x0a, 0x0a, + 0x44, 0x61, 0x74, 0x61, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, + 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, + 0x22, 0x57, 0x0a, 0x05, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x69, 0x67, + 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, + 0x74, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x69, 0x66, 0x66, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x64, 0x69, 0x66, 0x66, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x42, 0x79, 0x22, 0xc3, 0x01, 0x0a, 0x0d, 0x43, 0x61, + 0x75, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x76, 0x69, + 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x1d, 0x0a, + 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x19, 0x0a, 0x08, + 0x65, 0x6e, 0x64, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, + 0x65, 0x6e, 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x26, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x22, + 0x76, 0x0a, 0x04, 0x43, 0x56, 0x53, 0x53, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x32, 0x5f, 0x76, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x32, 0x56, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x12, 0x1b, 0x0a, 0x09, 0x76, 0x33, 0x5f, 0x76, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x76, 0x33, 0x56, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x12, 0x19, 0x0a, 0x08, 0x76, 0x32, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x01, 0x52, 0x07, 0x76, 0x32, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x12, 0x19, 0x0a, 0x08, + 0x76, 0x33, 0x5f, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, + 0x76, 0x33, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x22, 0x98, 0x01, 0x0a, 0x0e, 0x43, 0x75, 0x73, 0x74, + 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x1b, + 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x29, 0x0a, 0x05, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72, 0x69, + 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x52, + 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x12, 0x2a, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x04, 0x64, 0x61, + 0x74, 0x61, 0x22, 0xf3, 0x01, 0x0a, 0x04, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x0a, + 0x08, 0x69, 0x73, 0x5f, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x69, 0x73, 0x43, 0x61, 0x75, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x61, 0x6e, 0x6e, 0x6f, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x72, 0x75, 0x6e, + 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x74, 0x72, 0x75, + 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x68, 0x69, 0x67, 0x68, 0x6c, 0x69, + 0x67, 0x68, 0x74, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x68, 0x69, 0x67, + 0x68, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x72, 0x73, + 0x74, 0x5f, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0a, 0x66, + 0x69, 0x72, 0x73, 0x74, 0x43, 0x61, 0x75, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x61, 0x73, + 0x74, 0x5f, 0x63, 0x61, 0x75, 0x73, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x6c, + 0x61, 0x73, 0x74, 0x43, 0x61, 0x75, 0x73, 0x65, 0x22, 0x30, 0x0a, 0x04, 0x43, 0x6f, 0x64, 0x65, + 0x12, 0x28, 0x0a, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, + 0x69, 0x6e, 0x65, 0x52, 0x05, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x9f, 0x02, 0x0a, 0x0d, 0x53, + 0x65, 0x63, 0x72, 0x65, 0x74, 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x17, 0x0a, 0x07, + 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, + 0x75, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, + 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, + 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x14, 0x0a, + 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, + 0x74, 0x6c, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6c, 0x69, 0x6e, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4c, 0x69, + 0x6e, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x4c, 0x69, 0x6e, 0x65, 0x12, 0x26, 0x0a, + 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, + 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x64, 0x65, 0x52, + 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x12, 0x29, 0x0a, 0x05, 0x6c, + 0x61, 0x79, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72, 0x69, + 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x52, + 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x4a, 0x04, 0x08, 0x09, 0x10, 0x0a, 0x22, 0x5d, 0x0a, 0x06, + 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, + 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x70, 0x61, + 0x74, 0x68, 0x12, 0x37, 0x0a, 0x08, 0x66, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, + 0x67, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x99, 0x02, 0x0a, 0x0f, + 0x44, 0x65, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x12, + 0x32, 0x0a, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0e, 0x32, 0x16, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, + 0x2e, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x52, 0x08, 0x73, 0x65, 0x76, 0x65, 0x72, + 0x69, 0x74, 0x79, 0x12, 0x3e, 0x0a, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, + 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x43, 0x61, 0x74, 0x65, + 0x67, 0x6f, 0x72, 0x79, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, + 0x6f, 0x72, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6b, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6b, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, + 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x02, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6c, + 0x69, 0x6e, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x22, 0xed, 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x63, 0x65, + 0x6e, 0x73, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x41, 0x0a, 0x0c, 0x6c, 0x69, 0x63, 0x65, 0x6e, + 0x73, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, + 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x63, + 0x65, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x0b, 0x6c, + 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, + 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, + 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6b, 0x67, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6b, 0x67, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x66, 0x69, 0x6e, 0x67, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x46, 0x69, 0x6e, 0x64, 0x69, + 0x6e, 0x67, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x67, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x29, 0x0a, 0x05, + 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72, + 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x61, 0x79, 0x65, 0x72, + 0x52, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x22, 0x98, 0x01, 0x0a, 0x0e, 0x4c, 0x69, 0x63, 0x65, + 0x6e, 0x73, 0x65, 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x12, 0x3e, 0x0a, 0x08, 0x63, 0x61, + 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x2e, 0x45, 0x6e, 0x75, 0x6d, - 0x52, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x19, 0x0a, 0x08, 0x70, 0x6b, - 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x6b, - 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, - 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, - 0x74, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x64, - 0x65, 0x6e, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, - 0x78, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x22, 0xed, - 0x01, 0x0a, 0x0b, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x41, - 0x0a, 0x0c, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1e, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x2e, - 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x0b, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, 0x68, 0x12, 0x19, - 0x0a, 0x08, 0x70, 0x6b, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x70, 0x6b, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x08, 0x66, 0x69, 0x6e, - 0x67, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x72, - 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e, - 0x73, 0x65, 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x67, 0x69, - 0x6e, 0x67, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x2e, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x52, 0x05, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x22, 0x98, - 0x01, 0x0a, 0x0e, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x46, 0x69, 0x6e, 0x64, 0x69, 0x6e, - 0x67, 0x12, 0x3e, 0x0a, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, - 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, - 0x72, 0x79, 0x2e, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, - 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, - 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x02, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x64, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x22, 0x95, 0x01, 0x0a, 0x0f, 0x4c, 0x69, - 0x63, 0x65, 0x6e, 0x73, 0x65, 0x43, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x22, 0x81, 0x01, - 0x0a, 0x04, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x42, 0x49, - 0x44, 0x44, 0x45, 0x4e, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x52, 0x45, 0x53, 0x54, 0x52, 0x49, - 0x43, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x52, 0x45, 0x43, 0x49, 0x50, 0x52, - 0x4f, 0x43, 0x41, 0x4c, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x54, 0x49, 0x43, 0x45, - 0x10, 0x04, 0x12, 0x0e, 0x0a, 0x0a, 0x50, 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x56, 0x45, - 0x10, 0x05, 0x12, 0x10, 0x0a, 0x0c, 0x55, 0x4e, 0x45, 0x4e, 0x43, 0x55, 0x4d, 0x42, 0x45, 0x52, - 0x45, 0x44, 0x10, 0x06, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, - 0x07, 0x22, 0x4e, 0x0a, 0x0b, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, - 0x22, 0x3f, 0x0a, 0x04, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, - 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x50, 0x4b, - 0x47, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x10, 0x02, 0x12, - 0x10, 0x0a, 0x0c, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, - 0x03, 0x2a, 0x44, 0x0a, 0x08, 0x53, 0x65, 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x0b, 0x0a, - 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4c, 0x4f, - 0x57, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x45, 0x44, 0x49, 0x55, 0x4d, 0x10, 0x02, 0x12, - 0x08, 0x0a, 0x04, 0x48, 0x49, 0x47, 0x48, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x52, 0x49, - 0x54, 0x49, 0x43, 0x41, 0x4c, 0x10, 0x04, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x71, 0x75, 0x61, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, - 0x74, 0x79, 0x2f, 0x74, 0x72, 0x69, 0x76, 0x79, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x6f, 0x6d, - 0x6d, 0x6f, 0x6e, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x52, 0x08, 0x63, 0x61, 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, + 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x02, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x69, + 0x6e, 0x6b, 0x22, 0x95, 0x01, 0x0a, 0x0f, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x43, 0x61, + 0x74, 0x65, 0x67, 0x6f, 0x72, 0x79, 0x22, 0x81, 0x01, 0x0a, 0x04, 0x45, 0x6e, 0x75, 0x6d, 0x12, + 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, + 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x42, 0x49, 0x44, 0x44, 0x45, 0x4e, 0x10, 0x01, 0x12, + 0x0e, 0x0a, 0x0a, 0x52, 0x45, 0x53, 0x54, 0x52, 0x49, 0x43, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, + 0x0e, 0x0a, 0x0a, 0x52, 0x45, 0x43, 0x49, 0x50, 0x52, 0x4f, 0x43, 0x41, 0x4c, 0x10, 0x03, 0x12, + 0x0a, 0x0a, 0x06, 0x4e, 0x4f, 0x54, 0x49, 0x43, 0x45, 0x10, 0x04, 0x12, 0x0e, 0x0a, 0x0a, 0x50, + 0x45, 0x52, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x56, 0x45, 0x10, 0x05, 0x12, 0x10, 0x0a, 0x0c, 0x55, + 0x4e, 0x45, 0x4e, 0x43, 0x55, 0x4d, 0x42, 0x45, 0x52, 0x45, 0x44, 0x10, 0x06, 0x12, 0x0b, 0x0a, + 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x07, 0x22, 0x4e, 0x0a, 0x0b, 0x4c, 0x69, + 0x63, 0x65, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x22, 0x3f, 0x0a, 0x04, 0x45, 0x6e, 0x75, + 0x6d, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, + 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x50, 0x4b, 0x47, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, + 0x48, 0x45, 0x41, 0x44, 0x45, 0x52, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x4c, 0x49, 0x43, 0x45, + 0x4e, 0x53, 0x45, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x03, 0x2a, 0x44, 0x0a, 0x08, 0x53, 0x65, + 0x76, 0x65, 0x72, 0x69, 0x74, 0x79, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, + 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4c, 0x4f, 0x57, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, + 0x4d, 0x45, 0x44, 0x49, 0x55, 0x4d, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x49, 0x47, 0x48, + 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x52, 0x49, 0x54, 0x49, 0x43, 0x41, 0x4c, 0x10, 0x04, + 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, + 0x71, 0x75, 0x61, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2f, 0x74, 0x72, 0x69, 0x76, + 0x79, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x3b, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2852,42 +2840,41 @@ var file_rpc_common_service_proto_depIdxs = []int32{ 11, // 5: trivy.common.Misconfiguration.successes:type_name -> trivy.common.MisconfResult 11, // 6: trivy.common.Misconfiguration.warnings:type_name -> trivy.common.MisconfResult 11, // 7: trivy.common.Misconfiguration.failures:type_name -> trivy.common.MisconfResult - 11, // 8: trivy.common.Misconfiguration.exceptions:type_name -> trivy.common.MisconfResult - 12, // 9: trivy.common.MisconfResult.policy_metadata:type_name -> trivy.common.PolicyMetadata - 17, // 10: trivy.common.MisconfResult.cause_metadata:type_name -> trivy.common.CauseMetadata - 0, // 11: trivy.common.DetectedMisconfiguration.severity:type_name -> trivy.common.Severity - 16, // 12: trivy.common.DetectedMisconfiguration.layer:type_name -> trivy.common.Layer - 17, // 13: trivy.common.DetectedMisconfiguration.cause_metadata:type_name -> trivy.common.CauseMetadata - 0, // 14: trivy.common.Vulnerability.severity:type_name -> trivy.common.Severity - 8, // 15: trivy.common.Vulnerability.pkg_identifier:type_name -> trivy.common.PkgIdentifier - 16, // 16: trivy.common.Vulnerability.layer:type_name -> trivy.common.Layer - 29, // 17: trivy.common.Vulnerability.cvss:type_name -> trivy.common.Vulnerability.CvssEntry - 31, // 18: trivy.common.Vulnerability.published_date:type_name -> google.protobuf.Timestamp - 31, // 19: trivy.common.Vulnerability.last_modified_date:type_name -> google.protobuf.Timestamp - 32, // 20: trivy.common.Vulnerability.custom_advisory_data:type_name -> google.protobuf.Value - 32, // 21: trivy.common.Vulnerability.custom_vuln_data:type_name -> google.protobuf.Value - 15, // 22: trivy.common.Vulnerability.data_source:type_name -> trivy.common.DataSource - 30, // 23: trivy.common.Vulnerability.vendor_severity:type_name -> trivy.common.Vulnerability.VendorSeverityEntry - 21, // 24: trivy.common.CauseMetadata.code:type_name -> trivy.common.Code - 16, // 25: trivy.common.CustomResource.layer:type_name -> trivy.common.Layer - 32, // 26: trivy.common.CustomResource.data:type_name -> google.protobuf.Value - 20, // 27: trivy.common.Code.lines:type_name -> trivy.common.Line - 21, // 28: trivy.common.SecretFinding.code:type_name -> trivy.common.Code - 16, // 29: trivy.common.SecretFinding.layer:type_name -> trivy.common.Layer - 22, // 30: trivy.common.Secret.findings:type_name -> trivy.common.SecretFinding - 0, // 31: trivy.common.DetectedLicense.severity:type_name -> trivy.common.Severity - 1, // 32: trivy.common.DetectedLicense.category:type_name -> trivy.common.LicenseCategory.Enum - 2, // 33: trivy.common.LicenseFile.license_type:type_name -> trivy.common.LicenseType.Enum - 26, // 34: trivy.common.LicenseFile.fingings:type_name -> trivy.common.LicenseFinding - 16, // 35: trivy.common.LicenseFile.layer:type_name -> trivy.common.Layer - 1, // 36: trivy.common.LicenseFinding.category:type_name -> trivy.common.LicenseCategory.Enum - 18, // 37: trivy.common.Vulnerability.CvssEntry.value:type_name -> trivy.common.CVSS - 0, // 38: trivy.common.Vulnerability.VendorSeverityEntry.value:type_name -> trivy.common.Severity - 39, // [39:39] is the sub-list for method output_type - 39, // [39:39] is the sub-list for method input_type - 39, // [39:39] is the sub-list for extension type_name - 39, // [39:39] is the sub-list for extension extendee - 0, // [0:39] is the sub-list for field type_name + 12, // 8: trivy.common.MisconfResult.policy_metadata:type_name -> trivy.common.PolicyMetadata + 17, // 9: trivy.common.MisconfResult.cause_metadata:type_name -> trivy.common.CauseMetadata + 0, // 10: trivy.common.DetectedMisconfiguration.severity:type_name -> trivy.common.Severity + 16, // 11: trivy.common.DetectedMisconfiguration.layer:type_name -> trivy.common.Layer + 17, // 12: trivy.common.DetectedMisconfiguration.cause_metadata:type_name -> trivy.common.CauseMetadata + 0, // 13: trivy.common.Vulnerability.severity:type_name -> trivy.common.Severity + 8, // 14: trivy.common.Vulnerability.pkg_identifier:type_name -> trivy.common.PkgIdentifier + 16, // 15: trivy.common.Vulnerability.layer:type_name -> trivy.common.Layer + 29, // 16: trivy.common.Vulnerability.cvss:type_name -> trivy.common.Vulnerability.CvssEntry + 31, // 17: trivy.common.Vulnerability.published_date:type_name -> google.protobuf.Timestamp + 31, // 18: trivy.common.Vulnerability.last_modified_date:type_name -> google.protobuf.Timestamp + 32, // 19: trivy.common.Vulnerability.custom_advisory_data:type_name -> google.protobuf.Value + 32, // 20: trivy.common.Vulnerability.custom_vuln_data:type_name -> google.protobuf.Value + 15, // 21: trivy.common.Vulnerability.data_source:type_name -> trivy.common.DataSource + 30, // 22: trivy.common.Vulnerability.vendor_severity:type_name -> trivy.common.Vulnerability.VendorSeverityEntry + 21, // 23: trivy.common.CauseMetadata.code:type_name -> trivy.common.Code + 16, // 24: trivy.common.CustomResource.layer:type_name -> trivy.common.Layer + 32, // 25: trivy.common.CustomResource.data:type_name -> google.protobuf.Value + 20, // 26: trivy.common.Code.lines:type_name -> trivy.common.Line + 21, // 27: trivy.common.SecretFinding.code:type_name -> trivy.common.Code + 16, // 28: trivy.common.SecretFinding.layer:type_name -> trivy.common.Layer + 22, // 29: trivy.common.Secret.findings:type_name -> trivy.common.SecretFinding + 0, // 30: trivy.common.DetectedLicense.severity:type_name -> trivy.common.Severity + 1, // 31: trivy.common.DetectedLicense.category:type_name -> trivy.common.LicenseCategory.Enum + 2, // 32: trivy.common.LicenseFile.license_type:type_name -> trivy.common.LicenseType.Enum + 26, // 33: trivy.common.LicenseFile.fingings:type_name -> trivy.common.LicenseFinding + 16, // 34: trivy.common.LicenseFile.layer:type_name -> trivy.common.Layer + 1, // 35: trivy.common.LicenseFinding.category:type_name -> trivy.common.LicenseCategory.Enum + 18, // 36: trivy.common.Vulnerability.CvssEntry.value:type_name -> trivy.common.CVSS + 0, // 37: trivy.common.Vulnerability.VendorSeverityEntry.value:type_name -> trivy.common.Severity + 38, // [38:38] is the sub-list for method output_type + 38, // [38:38] is the sub-list for method input_type + 38, // [38:38] is the sub-list for extension type_name + 38, // [38:38] is the sub-list for extension extendee + 0, // [0:38] is the sub-list for field type_name } func init() { file_rpc_common_service_proto_init() } diff --git a/rpc/common/service.proto b/rpc/common/service.proto index d80bf57e3b86..e989738c285b 100644 --- a/rpc/common/service.proto +++ b/rpc/common/service.proto @@ -73,7 +73,8 @@ message Misconfiguration { repeated MisconfResult successes = 3; repeated MisconfResult warnings = 4; repeated MisconfResult failures = 5; - repeated MisconfResult exceptions = 6; + + reserved 6; // deprecated 'exceptions' } message MisconfResult { From 6fab88dd56c257ef2cc63b617c2a5decb1c4cf98 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Wed, 30 Oct 2024 04:47:26 +0600 Subject: [PATCH 058/108] fix(helm): properly handle multiple archived dependencies (#7782) Signed-off-by: nikpivkin --- pkg/iac/scanners/helm/parser/parser.go | 47 ++++++++++++------ pkg/iac/scanners/helm/parser/parser_test.go | 30 +++++++++++ .../multiple-archived-deps/Chart.yaml | 14 ++++++ .../charts/common-2.26.0.tgz | Bin 0 -> 15573 bytes .../opentelemetry-collector-0.108.0.tgz | Bin 0 -> 34993 bytes .../testdata/non-deps-archives/Chart.yaml | 14 ++++++ .../backup_charts/mysql-operator-2.2.2.tgz | Bin 0 -> 11892 bytes .../wordpress-operator-0.12.4.tgz | Bin 0 -> 38731 bytes 8 files changed, 89 insertions(+), 16 deletions(-) create mode 100644 pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/Chart.yaml create mode 100644 pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/charts/common-2.26.0.tgz create mode 100644 pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/charts/opentelemetry-collector-0.108.0.tgz create mode 100644 pkg/iac/scanners/helm/parser/testdata/non-deps-archives/Chart.yaml create mode 100644 pkg/iac/scanners/helm/parser/testdata/non-deps-archives/backup_charts/mysql-operator-2.2.2.tgz create mode 100644 pkg/iac/scanners/helm/parser/testdata/non-deps-archives/backup_charts/wordpress-operator-0.12.4.tgz diff --git a/pkg/iac/scanners/helm/parser/parser.go b/pkg/iac/scanners/helm/parser/parser.go index e38f032b6221..1a132fdc2f76 100644 --- a/pkg/iac/scanners/helm/parser/parser.go +++ b/pkg/iac/scanners/helm/parser/parser.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io/fs" + "path" "path/filepath" "regexp" "sort" @@ -46,7 +47,7 @@ type ChartFile struct { ManifestContent string } -func New(path string, opts ...Option) (*Parser, error) { +func New(src string, opts ...Option) (*Parser, error) { client := action.NewInstall(&action.Configuration{}) client.DryRun = true // don't do anything @@ -55,7 +56,7 @@ func New(path string, opts ...Option) (*Parser, error) { p := &Parser{ helmClient: client, - ChartSource: path, + ChartSource: src, logger: log.WithPrefix("helm parser"), } @@ -79,10 +80,10 @@ func New(path string, opts ...Option) (*Parser, error) { return p, nil } -func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) error { - p.workingFS = target +func (p *Parser) ParseFS(ctx context.Context, fsys fs.FS, target string) error { + p.workingFS = fsys - if err := fs.WalkDir(p.workingFS, filepath.ToSlash(path), func(path string, entry fs.DirEntry, err error) error { + if err := fs.WalkDir(p.workingFS, filepath.ToSlash(target), func(filePath string, entry fs.DirEntry, err error) error { select { case <-ctx.Done(): return ctx.Err() @@ -95,16 +96,20 @@ func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) error { return nil } - if detection.IsArchive(path) { - tarFS, err := p.addTarToFS(path) + if _, err := fs.Stat(p.workingFS, filePath); err != nil { + return nil + } + + if detection.IsArchive(filePath) && !isDependencyChartArchive(p.workingFS, filePath) { + tarFS, err := p.addTarToFS(filePath) if errors.Is(err, errSkipFS) { // an unpacked Chart already exists return nil } else if err != nil { - return fmt.Errorf("failed to add tar %q to FS: %w", path, err) + return fmt.Errorf("failed to add tar %q to FS: %w", filePath, err) } - targetPath := filepath.Dir(path) + targetPath := filepath.Dir(filePath) if targetPath == "" { targetPath = "." } @@ -114,7 +119,7 @@ func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) error { } return nil } else { - return p.addPaths(path) + return p.addPaths(filePath) } }); err != nil { return fmt.Errorf("walk dir error: %w", err) @@ -123,19 +128,29 @@ func (p *Parser) ParseFS(ctx context.Context, target fs.FS, path string) error { return nil } +func isDependencyChartArchive(fsys fs.FS, archivePath string) bool { + parent := path.Dir(archivePath) + if path.Base(parent) != "charts" { + return false + } + + _, err := fs.Stat(fsys, path.Join(parent, "..", "Chart.yaml")) + return err == nil +} + func (p *Parser) addPaths(paths ...string) error { - for _, path := range paths { - if _, err := fs.Stat(p.workingFS, path); err != nil { + for _, filePath := range paths { + if _, err := fs.Stat(p.workingFS, filePath); err != nil { return err } - if strings.HasSuffix(path, "Chart.yaml") && p.rootPath == "" { - if err := p.extractChartName(path); err != nil { + if strings.HasSuffix(filePath, "Chart.yaml") && p.rootPath == "" { + if err := p.extractChartName(filePath); err != nil { return err } - p.rootPath = filepath.Dir(path) + p.rootPath = filepath.Dir(filePath) } - p.filepaths = append(p.filepaths, path) + p.filepaths = append(p.filepaths, filePath) } return nil } diff --git a/pkg/iac/scanners/helm/parser/parser_test.go b/pkg/iac/scanners/helm/parser/parser_test.go index 9c8b05ce7696..01aa697d559c 100644 --- a/pkg/iac/scanners/helm/parser/parser_test.go +++ b/pkg/iac/scanners/helm/parser/parser_test.go @@ -48,4 +48,34 @@ func TestParseFS(t *testing.T) { } assert.Equal(t, expectedFiles, p.filepaths) }) + + t.Run("chart with multiple archived deps", func(t *testing.T) { + p, err := New(".") + require.NoError(t, err) + + fsys := os.DirFS(filepath.Join("testdata", "multiple-archived-deps")) + require.NoError(t, p.ParseFS(context.TODO(), fsys, ".")) + + expectedFiles := []string{ + "Chart.yaml", + "charts/common-2.26.0.tgz", + "charts/opentelemetry-collector-0.108.0.tgz", + } + assert.Equal(t, expectedFiles, p.filepaths) + }) + + t.Run("archives are not dependencies", func(t *testing.T) { + p, err := New(".") + require.NoError(t, err) + + fsys := os.DirFS(filepath.Join("testdata", "non-deps-archives")) + require.NoError(t, p.ParseFS(context.TODO(), fsys, ".")) + + expectedFiles := []string{ + "Chart.yaml", + "backup_charts/wordpress-operator/Chart.yaml", + "backup_charts/mysql-operator/Chart.yaml", + } + assert.Subset(t, p.filepaths, expectedFiles) + }) } diff --git a/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/Chart.yaml b/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/Chart.yaml new file mode 100644 index 000000000000..82d6c918088f --- /dev/null +++ b/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/Chart.yaml @@ -0,0 +1,14 @@ +apiVersion: v2 +appVersion: "1.1" +description: Test Chart +name: y-chart +version: 1.0.0 +kubeVersion: ">=1.21" + +dependencies: + - name: common + repository: https://charts.bitnami.com/bitnami + version: 2.26.0 + - name: opentelemetry-collector + version: 0.108.0 + repository: https://open-telemetry.github.io/opentelemetry-helm-charts \ No newline at end of file diff --git a/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/charts/common-2.26.0.tgz b/pkg/iac/scanners/helm/parser/testdata/multiple-archived-deps/charts/common-2.26.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..43f85ba36a9160d8ee712e11c22de7732a8f53db GIT binary patch literal 15573 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 X(}Twu8&x6#fdIf*neT%*tcmy!-ZISW literal 0 HcmV?d00001 diff --git a/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/Chart.yaml b/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/Chart.yaml new file mode 100644 index 000000000000..65225b732935 --- /dev/null +++ b/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/Chart.yaml @@ -0,0 +1,14 @@ +apiVersion: v2 +appVersion: "1.1" +description: Test Chart +name: y-chart +version: 1.0.0 +kubeVersion: ">=1.21" + +dependencies: + - name: mysql-operator + repository: https://mysql.github.io/mysql-operator/ + version: 2.2.2 + - name: wordpress-operator + version: 0.12.4 + repository: https://helm-charts.bitpoke.io \ No newline at end of file diff --git a/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/backup_charts/mysql-operator-2.2.2.tgz b/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/backup_charts/mysql-operator-2.2.2.tgz new file mode 100644 index 0000000000000000000000000000000000000000..b80fc3c0425e174020f5829338d436dceda8b322 GIT binary patch literal 11892 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 literal 0 HcmV?d00001 diff --git a/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/backup_charts/wordpress-operator-0.12.4.tgz b/pkg/iac/scanners/helm/parser/testdata/non-deps-archives/backup_charts/wordpress-operator-0.12.4.tgz new file mode 100644 index 0000000000000000000000000000000000000000..4086e9f1a6bab460c3517d28d39566d0226371d2 GIT binary patch literal 38731 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 literal 0 HcmV?d00001 From b661d680ff0372c8e4beea0db13bf69d6a2203a8 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Wed, 30 Oct 2024 16:16:02 +0600 Subject: [PATCH 059/108] fix(cli): add config name to skip-policy-update alias (#7820) Signed-off-by: nikpivkin --- pkg/flag/rego_flags.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/flag/rego_flags.go b/pkg/flag/rego_flags.go index 4b291f0a5eb3..84e0aed910f4 100644 --- a/pkg/flag/rego_flags.go +++ b/pkg/flag/rego_flags.go @@ -19,6 +19,7 @@ var ( Aliases: []Alias{ { Name: "skip-policy-update", + ConfigName: "rego.skip-policy-update", Deprecated: true, }, }, From f2bb9c6227743dd61f44eb591d4b15192fe110c6 Mon Sep 17 00:00:00 2001 From: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> Date: Wed, 30 Oct 2024 16:32:40 +0600 Subject: [PATCH 060/108] fix(sbom): use `Annotation` instead of `AttributionTexts` for `SPDX` formats (#7811) --- integration/testdata/conda-spdx.json.golden | 39 +++-- integration/testdata/julia-spdx.json.golden | 93 +++++++--- pkg/sbom/spdx/marshal.go | 42 +++-- pkg/sbom/spdx/marshal_test.go | 184 +++++++++++--------- pkg/sbom/spdx/testdata/happy/bom.json | 98 +++++++++-- pkg/sbom/spdx/unmarshal.go | 18 +- 6 files changed, 316 insertions(+), 158 deletions(-) diff --git a/integration/testdata/conda-spdx.json.golden b/integration/testdata/conda-spdx.json.golden index c14d1bf4e018..c11f52eeca1b 100644 --- a/integration/testdata/conda-spdx.json.golden +++ b/integration/testdata/conda-spdx.json.golden @@ -31,10 +31,15 @@ "referenceLocator": "pkg:conda/openssl@1.1.1q" } ], - "attributionTexts": [ - "PkgType: conda-pkg" - ], - "primaryPackagePurpose": "LIBRARY" + "primaryPackagePurpose": "LIBRARY", + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "PkgType: conda-pkg" + } + ] }, { "name": "pip", @@ -55,20 +60,30 @@ "referenceLocator": "pkg:conda/pip@22.2.2" } ], - "attributionTexts": [ - "PkgType: conda-pkg" - ], - "primaryPackagePurpose": "LIBRARY" + "primaryPackagePurpose": "LIBRARY", + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "PkgType: conda-pkg" + } + ] }, { "name": "testdata/fixtures/repo/conda", "SPDXID": "SPDXRef-Filesystem-2e2426fd0f2580ef", "downloadLocation": "NONE", "filesAnalyzed": false, - "attributionTexts": [ - "SchemaVersion: 2" - ], - "primaryPackagePurpose": "SOURCE" + "primaryPackagePurpose": "SOURCE", + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "SchemaVersion: 2" + } + ] } ], "files": [ diff --git a/integration/testdata/julia-spdx.json.golden b/integration/testdata/julia-spdx.json.golden index 89a4edb3f287..a770765dd849 100644 --- a/integration/testdata/julia-spdx.json.golden +++ b/integration/testdata/julia-spdx.json.golden @@ -17,11 +17,21 @@ "SPDXID": "SPDXRef-Application-18fc3597717a3e56", "downloadLocation": "NONE", "filesAnalyzed": false, - "attributionTexts": [ - "Class: lang-pkgs", - "Type: julia" - ], - "primaryPackagePurpose": "APPLICATION" + "primaryPackagePurpose": "APPLICATION", + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "Class: lang-pkgs" + }, + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "Type: julia" + } + ] }, { "name": "A", @@ -40,11 +50,21 @@ "referenceLocator": "pkg:julia/A@1.9.0?uuid=ead4f63c-334e-11e9-00e6-e7f0a5f21b60" } ], - "attributionTexts": [ - "PkgID: ead4f63c-334e-11e9-00e6-e7f0a5f21b60", - "PkgType: julia" - ], - "primaryPackagePurpose": "LIBRARY" + "primaryPackagePurpose": "LIBRARY", + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "PkgID: ead4f63c-334e-11e9-00e6-e7f0a5f21b60" + }, + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "PkgType: julia" + } + ] }, { "name": "B", @@ -63,11 +83,21 @@ "referenceLocator": "pkg:julia/B@1.9.0?uuid=f41f7b98-334e-11e9-1257-49272045fb24" } ], - "attributionTexts": [ - "PkgID: f41f7b98-334e-11e9-1257-49272045fb24", - "PkgType: julia" - ], - "primaryPackagePurpose": "LIBRARY" + "primaryPackagePurpose": "LIBRARY", + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "PkgID: f41f7b98-334e-11e9-1257-49272045fb24" + }, + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "PkgType: julia" + } + ] }, { "name": "B", @@ -86,21 +116,36 @@ "referenceLocator": "pkg:julia/B@1.9.0?uuid=edca9bc6-334e-11e9-3554-9595dbb4349c" } ], - "attributionTexts": [ - "PkgID: edca9bc6-334e-11e9-3554-9595dbb4349c", - "PkgType: julia" - ], - "primaryPackagePurpose": "LIBRARY" + "primaryPackagePurpose": "LIBRARY", + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "PkgID: edca9bc6-334e-11e9-3554-9595dbb4349c" + }, + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "PkgType: julia" + } + ] }, { "name": "testdata/fixtures/repo/julia", "SPDXID": "SPDXRef-Filesystem-1be792dd0077c431", "downloadLocation": "NONE", "filesAnalyzed": false, - "attributionTexts": [ - "SchemaVersion: 2" - ], - "primaryPackagePurpose": "SOURCE" + "primaryPackagePurpose": "SOURCE", + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2021-08-25T12:20:30Z", + "annotationType": "OTHER", + "comment": "SchemaVersion: 2" + } + ] } ], "relationships": [ diff --git a/pkg/sbom/spdx/marshal.go b/pkg/sbom/spdx/marshal.go index 809ce95d7e4c..51f9144f682d 100644 --- a/pkg/sbom/spdx/marshal.go +++ b/pkg/sbom/spdx/marshal.go @@ -50,6 +50,8 @@ const ( PackageSupplierNoAssertion = "NOASSERTION" PackageSupplierOrganization = "Organization" + PackageAnnotatorToolField = "Tool" + RelationShipContains = common.TypeRelationshipContains RelationShipDescribe = common.TypeRelationshipDescribe RelationShipDependsOn = common.TypeRelationshipDependsOn @@ -122,6 +124,9 @@ func (m *Marshaler) Marshal(ctx context.Context, bom *core.BOM) (*spdx.Document, packages []*spdx.Package ) + // Lock time to use same time for all spdx fields + timeNow := clock.Now(ctx).UTC().Format(time.RFC3339) + root := bom.Root() pkgDownloadLocation := m.packageDownloadLocation(root) @@ -129,7 +134,7 @@ func (m *Marshaler) Marshal(ctx context.Context, bom *core.BOM) (*spdx.Document, packageIDs := make(map[uuid.UUID]spdx.ElementID) // Root package contains OS, OS packages, language-specific packages and so on. - rootPkg, err := m.rootSPDXPackage(root, pkgDownloadLocation) + rootPkg, err := m.rootSPDXPackage(root, timeNow, pkgDownloadLocation) if err != nil { return nil, xerrors.Errorf("failed to generate a root package: %w", err) } @@ -144,7 +149,7 @@ func (m *Marshaler) Marshal(ctx context.Context, bom *core.BOM) (*spdx.Document, if c.Root { continue } - spdxPackage, err := m.spdxPackage(c, pkgDownloadLocation) + spdxPackage, err := m.spdxPackage(c, timeNow, pkgDownloadLocation) if err != nil { return nil, xerrors.Errorf("spdx package error: %w", err) } @@ -216,7 +221,7 @@ func (m *Marshaler) Marshal(ctx context.Context, bom *core.BOM) (*spdx.Document, CreatorType: "Tool", }, }, - Created: clock.Now(ctx).UTC().Format(time.RFC3339), + Created: timeNow, }, Packages: packages, Relationships: relationShips, @@ -237,7 +242,7 @@ func (m *Marshaler) packageDownloadLocation(root *core.Component) string { return location } -func (m *Marshaler) rootSPDXPackage(root *core.Component, pkgDownloadLocation string) (*spdx.Package, error) { +func (m *Marshaler) rootSPDXPackage(root *core.Component, timeNow, pkgDownloadLocation string) (*spdx.Package, error) { var externalReferences []*spdx.PackageExternalReference // When the target is a container image, add PURL to the external references of the root package. if root.PkgIdentifier.PURL != nil { @@ -258,17 +263,25 @@ func (m *Marshaler) rootSPDXPackage(root *core.Component, pkgDownloadLocation st PackageName: root.Name, PackageSPDXIdentifier: elementID(camelCase(string(root.Type)), pkgID), PackageDownloadLocation: pkgDownloadLocation, - PackageAttributionTexts: m.spdxAttributionTexts(root), + Annotations: m.spdxAnnotations(root, timeNow), PackageExternalReferences: externalReferences, PrimaryPackagePurpose: pkgPurpose, }, nil } -func (m *Marshaler) appendAttributionText(attributionTexts []string, key, value string) []string { +func (m *Marshaler) appendAnnotation(annotations []spdx.Annotation, timeNow, key, value string) []spdx.Annotation { if value == "" { - return attributionTexts + return annotations } - return append(attributionTexts, fmt.Sprintf("%s: %s", key, value)) + return append(annotations, spdx.Annotation{ + AnnotationDate: timeNow, + AnnotationType: spdx.CategoryOther, + Annotator: spdx.Annotator{ + Annotator: fmt.Sprintf("%s-%s", CreatorTool, m.appVersion), + AnnotatorType: PackageAnnotatorToolField, + }, + AnnotationComment: fmt.Sprintf("%s: %s", key, value), + }) } func (m *Marshaler) purlExternalReference(packageURL string) *spdx.PackageExternalReference { @@ -287,7 +300,7 @@ func (m *Marshaler) advisoryExternalReference(primaryURL string) *spdx.PackageEx } } -func (m *Marshaler) spdxPackage(c *core.Component, pkgDownloadLocation string) (spdx.Package, error) { +func (m *Marshaler) spdxPackage(c *core.Component, timeNow, pkgDownloadLocation string) (spdx.Package, error) { pkgID, err := calcPkgID(m.hasher, c) if err != nil { return spdx.Package{}, xerrors.Errorf("failed to get os metadata package ID: %w", err) @@ -343,7 +356,7 @@ func (m *Marshaler) spdxPackage(c *core.Component, pkgDownloadLocation string) ( PrimaryPackagePurpose: purpose, PackageDownloadLocation: pkgDownloadLocation, PackageExternalReferences: pkgExtRefs, - PackageAttributionTexts: m.spdxAttributionTexts(c), + Annotations: m.spdxAnnotations(c, timeNow), PackageSourceInfo: sourceInfo, PackageSupplier: supplier, PackageChecksums: m.spdxChecksums(digests), @@ -365,16 +378,15 @@ func spdxPkgName(component *core.Component) string { } return component.Name } - -func (m *Marshaler) spdxAttributionTexts(c *core.Component) []string { - var texts []string +func (m *Marshaler) spdxAnnotations(c *core.Component, timeNow string) []spdx.Annotation { + var annotations []spdx.Annotation for _, p := range c.Properties { // Add properties that are not in other fields. if !slices.Contains(duplicateProperties, p.Name) { - texts = m.appendAttributionText(texts, p.Name, p.Value) + annotations = m.appendAnnotation(annotations, timeNow, p.Name, p.Value) } } - return texts + return annotations } func (m *Marshaler) spdxLicense(c *core.Component) string { diff --git a/pkg/sbom/spdx/marshal_test.go b/pkg/sbom/spdx/marshal_test.go index 74e9bee0a45d..4d9d33c013a7 100644 --- a/pkg/sbom/spdx/marshal_test.go +++ b/pkg/sbom/spdx/marshal_test.go @@ -25,6 +25,20 @@ import ( "github.com/aquasecurity/trivy/pkg/uuid" ) +func annotation(t *testing.T, comment string) spdx.Annotation { + t.Helper() + + return spdx.Annotation{ + AnnotationDate: "2021-08-25T12:20:30Z", + AnnotationType: spdx.CategoryOther, + Annotator: spdx.Annotator{ + Annotator: "trivy-0.56.2", + AnnotatorType: tspdx.PackageAnnotatorToolField, + }, + AnnotationComment: comment, + } +} + func TestMarshaler_Marshal(t *testing.T) { testCases := []struct { name string @@ -164,7 +178,7 @@ func TestMarshaler_Marshal(t *testing.T) { CreatorType: "Organization", }, { - Creator: "trivy-0.38.1", + Creator: "trivy-0.56.2", CreatorType: "Tool", }, }, @@ -176,9 +190,9 @@ func TestMarshaler_Marshal(t *testing.T) { PackageDownloadLocation: "NONE", PackageName: "app/Gemfile.lock", PrimaryPackagePurpose: tspdx.PackagePurposeApplication, - PackageAttributionTexts: []string{ - "Class: lang-pkgs", - "Type: bundler", + Annotations: []spdx.Annotation{ + annotation(t, "Class: lang-pkgs"), + annotation(t, "Type: bundler"), }, }, { @@ -186,9 +200,9 @@ func TestMarshaler_Marshal(t *testing.T) { PackageDownloadLocation: "NONE", PackageName: "app/subproject/Gemfile.lock", PrimaryPackagePurpose: tspdx.PackagePurposeApplication, - PackageAttributionTexts: []string{ - "Class: lang-pkgs", - "Type: bundler", + Annotations: []spdx.Annotation{ + annotation(t, "Class: lang-pkgs"), + annotation(t, "Type: bundler"), }, }, { @@ -202,14 +216,14 @@ func TestMarshaler_Marshal(t *testing.T) { Locator: "pkg:oci/rails@sha256%3Aa27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177?arch=arm64&repository_url=index.docker.io%2Flibrary%2Frails", }, }, - PackageAttributionTexts: []string{ - "DiffID: sha256:d871dadfb37b53ef1ca45be04fc527562b91989991a8f545345ae3be0b93f92a", - "ImageID: sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6", - "Labels:vendor: aquasecurity", - "RepoDigest: rails@sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177", - "RepoTag: rails:latest", - "SchemaVersion: 2", - "Size: 1024", + Annotations: []spdx.Annotation{ + annotation(t, "DiffID: sha256:d871dadfb37b53ef1ca45be04fc527562b91989991a8f545345ae3be0b93f92a"), + annotation(t, "ImageID: sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6"), + annotation(t, "Labels:vendor: aquasecurity"), + annotation(t, "RepoDigest: rails@sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177"), + annotation(t, "RepoTag: rails:latest"), + annotation(t, "SchemaVersion: 2"), + annotation(t, "Size: 1024"), }, PrimaryPackagePurpose: tspdx.PackagePurposeContainer, }, @@ -220,8 +234,8 @@ func TestMarshaler_Marshal(t *testing.T) { PackageVersion: "7.0.1", PackageLicenseConcluded: "NOASSERTION", PackageLicenseDeclared: "NOASSERTION", - PackageAttributionTexts: []string{ - "PkgType: bundler", + Annotations: []spdx.Annotation{ + annotation(t, "PkgType: bundler"), }, PackageExternalReferences: []*spdx.PackageExternalReference{ { @@ -241,8 +255,8 @@ func TestMarshaler_Marshal(t *testing.T) { PackageVersion: "7.0.1", PackageLicenseConcluded: "NOASSERTION", PackageLicenseDeclared: "NOASSERTION", - PackageAttributionTexts: []string{ - "PkgType: bundler", + Annotations: []spdx.Annotation{ + annotation(t, "PkgType: bundler"), }, PackageExternalReferences: []*spdx.PackageExternalReference{ { @@ -262,8 +276,8 @@ func TestMarshaler_Marshal(t *testing.T) { PackageVersion: "7.0.1", PackageLicenseConcluded: "NOASSERTION", PackageLicenseDeclared: "NOASSERTION", - PackageAttributionTexts: []string{ - "PkgType: bundler", + Annotations: []spdx.Annotation{ + annotation(t, "PkgType: bundler"), }, PackageExternalReferences: []*spdx.PackageExternalReference{ { @@ -283,8 +297,8 @@ func TestMarshaler_Marshal(t *testing.T) { PackageVersion: "2.30-93.el8", PackageLicenseConcluded: "GPL-3.0-or-later", PackageLicenseDeclared: "GPL-3.0-or-later", - PackageAttributionTexts: []string{ - "PkgType: centos", + Annotations: []spdx.Annotation{ + annotation(t, "PkgType: centos"), }, PackageSupplier: &spdx.Supplier{ SupplierType: tspdx.PackageSupplierOrganization, @@ -312,9 +326,9 @@ func TestMarshaler_Marshal(t *testing.T) { PackageName: "centos", PackageVersion: "8.3.2011", PrimaryPackagePurpose: tspdx.PackagePurposeOS, - PackageAttributionTexts: []string{ - "Class: os-pkgs", - "Type: centos", + Annotations: []spdx.Annotation{ + annotation(t, "Class: os-pkgs"), + annotation(t, "Type: centos"), }, }, }, @@ -486,7 +500,7 @@ func TestMarshaler_Marshal(t *testing.T) { CreatorType: "Organization", }, { - Creator: "trivy-0.38.1", + Creator: "trivy-0.56.2", CreatorType: "Tool", }, }, @@ -497,11 +511,11 @@ func TestMarshaler_Marshal(t *testing.T) { PackageName: "centos:latest", PackageSPDXIdentifier: "ContainerImage-413bfede37ad01fc", PackageDownloadLocation: "NONE", - PackageAttributionTexts: []string{ - "ImageID: sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6", - "RepoTag: centos:latest", - "SchemaVersion: 2", - "Size: 1024", + Annotations: []spdx.Annotation{ + annotation(t, "ImageID: sha256:5d0da3dc976460b72c77d94c8a1ad043720b0416bfc16c52c45d4847e53fadb6"), + annotation(t, "RepoTag: centos:latest"), + annotation(t, "SchemaVersion: 2"), + annotation(t, "Size: 1024"), }, PrimaryPackagePurpose: tspdx.PackagePurposeContainer, }, @@ -512,8 +526,8 @@ func TestMarshaler_Marshal(t *testing.T) { PackageVersion: "1:2.2.53-1.el8", PackageLicenseConcluded: "GPL-2.0-or-later", PackageLicenseDeclared: "GPL-2.0-or-later", - PackageAttributionTexts: []string{ - "PkgType: centos", + Annotations: []spdx.Annotation{ + annotation(t, "PkgType: centos"), }, PackageExternalReferences: []*spdx.PackageExternalReference{ { @@ -546,9 +560,9 @@ func TestMarshaler_Marshal(t *testing.T) { Locator: "pkg:gem/actionpack@7.0.1", }, }, - PackageAttributionTexts: []string{ - "LayerDiffID: sha256:ccb64cf0b7ba2e50741d0b64cae324eb5de3b1e2f580bbf177e721b67df38488", - "PkgType: gemspec", + Annotations: []spdx.Annotation{ + annotation(t, "LayerDiffID: sha256:ccb64cf0b7ba2e50741d0b64cae324eb5de3b1e2f580bbf177e721b67df38488"), + annotation(t, "PkgType: gemspec"), }, PrimaryPackagePurpose: tspdx.PackagePurposeLibrary, PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion}, @@ -571,9 +585,9 @@ func TestMarshaler_Marshal(t *testing.T) { Locator: "pkg:gem/actionpack@7.0.1", }, }, - PackageAttributionTexts: []string{ - "LayerDiffID: sha256:ccb64cf0b7ba2e50741d0b64cae324eb5de3b1e2f580bbf177e721b67df38488", - "PkgType: gemspec", + Annotations: []spdx.Annotation{ + annotation(t, "LayerDiffID: sha256:ccb64cf0b7ba2e50741d0b64cae324eb5de3b1e2f580bbf177e721b67df38488"), + annotation(t, "PkgType: gemspec"), }, PrimaryPackagePurpose: tspdx.PackagePurposeLibrary, PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion}, @@ -588,9 +602,9 @@ func TestMarshaler_Marshal(t *testing.T) { PackageName: "centos", PackageVersion: "8.3.2011", PrimaryPackagePurpose: tspdx.PackagePurposeOS, - PackageAttributionTexts: []string{ - "Class: os-pkgs", - "Type: centos", + Annotations: []spdx.Annotation{ + annotation(t, "Class: os-pkgs"), + annotation(t, "Type: centos"), }, }, }, @@ -719,7 +733,7 @@ func TestMarshaler_Marshal(t *testing.T) { CreatorType: "Organization", }, { - Creator: "trivy-0.38.1", + Creator: "trivy-0.56.2", CreatorType: "Tool", }, }, @@ -731,9 +745,9 @@ func TestMarshaler_Marshal(t *testing.T) { PackageDownloadLocation: "NONE", PackageName: "Gemfile.lock", PrimaryPackagePurpose: tspdx.PackagePurposeApplication, - PackageAttributionTexts: []string{ - "Class: lang-pkgs", - "Type: bundler", + Annotations: []spdx.Annotation{ + annotation(t, "Class: lang-pkgs"), + annotation(t, "Type: bundler"), }, }, { @@ -741,9 +755,9 @@ func TestMarshaler_Marshal(t *testing.T) { PackageDownloadLocation: "NONE", PackageName: "pom.xml", PrimaryPackagePurpose: tspdx.PackagePurposeApplication, - PackageAttributionTexts: []string{ - "Class: lang-pkgs", - "Type: pom", + Annotations: []spdx.Annotation{ + annotation(t, "Class: lang-pkgs"), + annotation(t, "Type: pom"), }, }, { @@ -763,8 +777,8 @@ func TestMarshaler_Marshal(t *testing.T) { PrimaryPackagePurpose: tspdx.PackagePurposeLibrary, PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion}, PackageSourceInfo: "package found in: Gemfile.lock", - PackageAttributionTexts: []string{ - "PkgType: bundler", + Annotations: []spdx.Annotation{ + annotation(t, "PkgType: bundler"), }, }, { @@ -784,17 +798,17 @@ func TestMarshaler_Marshal(t *testing.T) { PrimaryPackagePurpose: tspdx.PackagePurposeLibrary, PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion}, PackageSourceInfo: "package found in: pom.xml", - PackageAttributionTexts: []string{ - "PkgID: com.example:example:1.0.0", - "PkgType: pom", + Annotations: []spdx.Annotation{ + annotation(t, "PkgID: com.example:example:1.0.0"), + annotation(t, "PkgType: pom"), }, }, { PackageSPDXIdentifier: spdx.ElementID("Filesystem-5af0f1f08c20909a"), PackageDownloadLocation: "NONE", PackageName: "masahiro331/CVE-2021-41098", - PackageAttributionTexts: []string{ - "SchemaVersion: 2", + Annotations: []spdx.Annotation{ + annotation(t, "SchemaVersion: 2"), }, PrimaryPackagePurpose: tspdx.PackagePurposeSource, }, @@ -878,7 +892,7 @@ func TestMarshaler_Marshal(t *testing.T) { CreatorType: "Organization", }, { - Creator: "trivy-0.38.1", + Creator: "trivy-0.56.2", CreatorType: "Tool", }, }, @@ -906,16 +920,16 @@ func TestMarshaler_Marshal(t *testing.T) { }, PrimaryPackagePurpose: tspdx.PackagePurposeLibrary, PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion}, - PackageAttributionTexts: []string{ - "PkgType: jar", + Annotations: []spdx.Annotation{ + annotation(t, "PkgType: jar"), }, }, { PackageSPDXIdentifier: spdx.ElementID("Filesystem-121e7e7a43f02ab"), PackageDownloadLocation: "NONE", PackageName: "log4j-core-2.17.0.jar", - PackageAttributionTexts: []string{ - "SchemaVersion: 2", + Annotations: []spdx.Annotation{ + annotation(t, "SchemaVersion: 2"), }, PrimaryPackagePurpose: tspdx.PackagePurposeSource, }, @@ -980,7 +994,7 @@ func TestMarshaler_Marshal(t *testing.T) { CreatorType: "Organization", }, { - Creator: "trivy-0.38.1", + Creator: "trivy-0.56.2", CreatorType: "Tool", }, }, @@ -1001,9 +1015,9 @@ func TestMarshaler_Marshal(t *testing.T) { Locator: "pkg:npm/ruby-typeprof@0.20.1", }, }, - PackageAttributionTexts: []string{ - "LayerDiffID: sha256:661c3fd3cc16b34c070f3620ca6b03b6adac150f9a7e5d0e3c707a159990f88e", - "PkgType: node-pkg", + Annotations: []spdx.Annotation{ + annotation(t, "LayerDiffID: sha256:661c3fd3cc16b34c070f3620ca6b03b6adac150f9a7e5d0e3c707a159990f88e"), + annotation(t, "PkgType: node-pkg"), }, PrimaryPackagePurpose: tspdx.PackagePurposeLibrary, PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion}, @@ -1016,8 +1030,8 @@ func TestMarshaler_Marshal(t *testing.T) { PackageSPDXIdentifier: "Repository-1a78857c1a6a759e", PackageName: "http://test-aggregate", PackageDownloadLocation: "git+http://test-aggregate", - PackageAttributionTexts: []string{ - "SchemaVersion: 2", + Annotations: []spdx.Annotation{ + annotation(t, "SchemaVersion: 2"), }, PrimaryPackagePurpose: tspdx.PackagePurposeSource, }, @@ -1075,7 +1089,7 @@ func TestMarshaler_Marshal(t *testing.T) { CreatorType: "Organization", }, { - Creator: "trivy-0.38.1", + Creator: "trivy-0.56.2", CreatorType: "Tool", }, }, @@ -1086,8 +1100,8 @@ func TestMarshaler_Marshal(t *testing.T) { PackageName: "empty/path", PackageSPDXIdentifier: "Filesystem-70f34983067dba86", PackageDownloadLocation: "NONE", - PackageAttributionTexts: []string{ - "SchemaVersion: 2", + Annotations: []spdx.Annotation{ + annotation(t, "SchemaVersion: 2"), }, PrimaryPackagePurpose: tspdx.PackagePurposeSource, }, @@ -1137,7 +1151,7 @@ func TestMarshaler_Marshal(t *testing.T) { CreatorType: "Organization", }, { - Creator: "trivy-0.38.1", + Creator: "trivy-0.56.2", CreatorType: "Tool", }, }, @@ -1148,8 +1162,8 @@ func TestMarshaler_Marshal(t *testing.T) { PackageName: "secret", PackageSPDXIdentifier: "Filesystem-5c08d34162a2c5d3", PackageDownloadLocation: "NONE", - PackageAttributionTexts: []string{ - "SchemaVersion: 2", + Annotations: []spdx.Annotation{ + annotation(t, "SchemaVersion: 2"), }, PrimaryPackagePurpose: tspdx.PackagePurposeSource, }, @@ -1209,7 +1223,7 @@ func TestMarshaler_Marshal(t *testing.T) { CreatorType: "Organization", }, { - Creator: "trivy-0.38.1", + Creator: "trivy-0.56.2", CreatorType: "Tool", }, }, @@ -1221,9 +1235,9 @@ func TestMarshaler_Marshal(t *testing.T) { PackageDownloadLocation: "NONE", PackageName: "/usr/local/bin/test", PrimaryPackagePurpose: tspdx.PackagePurposeApplication, - PackageAttributionTexts: []string{ - "Class: lang-pkgs", - "Type: gobinary", + Annotations: []spdx.Annotation{ + annotation(t, "Class: lang-pkgs"), + annotation(t, "Type: gobinary"), }, }, { @@ -1235,8 +1249,8 @@ func TestMarshaler_Marshal(t *testing.T) { PrimaryPackagePurpose: tspdx.PackagePurposeLibrary, PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion}, PackageSourceInfo: "package found in: /usr/local/bin/test", - PackageAttributionTexts: []string{ - "PkgType: gobinary", + Annotations: []spdx.Annotation{ + annotation(t, "PkgType: gobinary"), }, }, { @@ -1256,16 +1270,16 @@ func TestMarshaler_Marshal(t *testing.T) { PrimaryPackagePurpose: tspdx.PackagePurposeLibrary, PackageSupplier: &spdx.Supplier{Supplier: tspdx.PackageSupplierNoAssertion}, PackageSourceInfo: "package found in: /usr/local/bin/test", - PackageAttributionTexts: []string{ - "PkgType: gobinary", + Annotations: []spdx.Annotation{ + annotation(t, "PkgType: gobinary"), }, }, { PackageName: "go-artifact", PackageSPDXIdentifier: "Filesystem-e340f27468b382be", PackageDownloadLocation: "NONE", - PackageAttributionTexts: []string{ - "SchemaVersion: 2", + Annotations: []spdx.Annotation{ + annotation(t, "SchemaVersion: 2"), }, PrimaryPackagePurpose: tspdx.PackagePurposeSource, }, @@ -1326,7 +1340,7 @@ func TestMarshaler_Marshal(t *testing.T) { ctx := clock.With(context.Background(), time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC)) uuid.SetFakeUUID(t, "3ff14136-e09f-4df9-80ea-%012d") - marshaler := tspdx.NewMarshaler("0.38.1", tspdx.WithHasher(hasher)) + marshaler := tspdx.NewMarshaler("0.56.2", tspdx.WithHasher(hasher)) spdxDoc, err := marshaler.MarshalReport(ctx, tc.inputReport) require.NoError(t, err) diff --git a/pkg/sbom/spdx/testdata/happy/bom.json b/pkg/sbom/spdx/testdata/happy/bom.json index 823c0471351f..88af63b3b71a 100644 --- a/pkg/sbom/spdx/testdata/happy/bom.json +++ b/pkg/sbom/spdx/testdata/happy/bom.json @@ -37,13 +37,43 @@ }, { "SPDXID": "SPDXRef-ContainerImage-b5d81cde5f95c8fc", - "attributionTexts": [ - "SchemaVersion: 2", - "ImageID: sha256:49193a2310dbad4c02382da87ac624a80a92387a4f7536235f9ba590e5bcd7b5", - "DiffID: sha256:dd565ff850e7003356e2b252758f9bdc1ff2803f61e995e24c7844f6297f8fc3", - "DiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1", - "RepoTag: maven-test-project:latest", - "RepoTag: tmp-test:latest" + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "SchemaVersion: 2" + }, + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "ImageID: sha256:49193a2310dbad4c02382da87ac624a80a92387a4f7536235f9ba590e5bcd7b5" + }, + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "DiffID: sha256:dd565ff850e7003356e2b252758f9bdc1ff2803f61e995e24c7844f6297f8fc3" + }, + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "DiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + }, + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "RepoTag: maven-test-project:latest" + }, + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "RepoTag: tmp-test:latest" + } ], "filesAnalyzed": false, "name": "meven-test-project" @@ -56,8 +86,13 @@ }, { "SPDXID": "SPDXRef-Package-2906575950df652b", - "attributionTexts": [ - "LayerDiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "LayerDiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + } ], "externalRefs": [ { @@ -74,8 +109,13 @@ }, { "SPDXID": "SPDXRef-Package-2a53baa495b9ddaf", - "attributionTexts": [ - "LayerDiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "LayerDiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + } ], "externalRefs": [ { @@ -92,8 +132,13 @@ }, { "SPDXID": "SPDXRef-Package-5e2e255ac76747ef", - "attributionTexts": [ - "LayerDiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "LayerDiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + } ], "externalRefs": [ { @@ -110,8 +155,13 @@ }, { "SPDXID": "SPDXRef-Package-5f1dbaff8de5eb06", - "attributionTexts": [ - "LayerDiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "LayerDiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + } ], "externalRefs": [ { @@ -128,8 +178,13 @@ }, { "SPDXID": "SPDXRef-Package-84ebffe38343d949", - "attributionTexts": [ - "LayerDiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "LayerDiffID: sha256:3c79e832b1b4891a1cb4a326ef8524e0bd14a2537150ac0e203a5677176c1ca1" + } ], "externalRefs": [ { @@ -146,8 +201,13 @@ }, { "SPDXID": "SPDXRef-Package-b7ebaf0233f1ef7b", - "attributionTexts": [ - "LayerDiffID: sha256:dd565ff850e7003356e2b252758f9bdc1ff2803f61e995e24c7844f6297f8fc3" + "annotations": [ + { + "annotator": "Tool: trivy-dev", + "annotationDate": "2024-10-29T06:50:38Z", + "annotationType": "OTHER", + "comment": "LayerDiffID: sha256:dd565ff850e7003356e2b252758f9bdc1ff2803f61e995e24c7844f6297f8fc3" + } ], "externalRefs": [ { diff --git a/pkg/sbom/spdx/unmarshal.go b/pkg/sbom/spdx/unmarshal.go index d2ba05b44999..c7ed6b0905dd 100644 --- a/pkg/sbom/spdx/unmarshal.go +++ b/pkg/sbom/spdx/unmarshal.go @@ -194,9 +194,21 @@ func (s *SPDX) parsePackage(spdxPkg spdx.Package) (*core.Component, error) { } } - // Attributions - for _, attr := range spdxPkg.PackageAttributionTexts { - k, v, ok := strings.Cut(attr, ": ") + // Trivy stores properties in Annotations + // But previous versions stored properties in AttributionTexts + // So we need to check both cases to maintain backward compatibility + var props []string + if len(spdxPkg.Annotations) > 0 { + for _, annotation := range spdxPkg.Annotations { + props = append(props, annotation.AnnotationComment) + } + } else if len(spdxPkg.PackageAttributionTexts) > 0 { + for _, attr := range spdxPkg.PackageAttributionTexts { + props = append(props, attr) + } + } + for _, prop := range props { + k, v, ok := strings.Cut(prop, ": ") if !ok { continue } From 78827768a612ab305bf9c55409ce76d6774302a5 Mon Sep 17 00:00:00 2001 From: afdesk Date: Thu, 31 Oct 2024 07:14:56 +0600 Subject: [PATCH 061/108] fix(k8s): skip resources without misconfigs (#7797) --- pkg/k8s/report/report.go | 11 ++++++++++- pkg/k8s/report/report_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/pkg/k8s/report/report.go b/pkg/k8s/report/report.go index b52391ec8239..947d39de14b8 100644 --- a/pkg/k8s/report/report.go +++ b/pkg/k8s/report/report.go @@ -90,8 +90,13 @@ func (r Report) consolidate() ConsolidatedReport { for _, m := range r.Resources { if vulnerabilitiesOrSecretResource(m) { vulnerabilities = append(vulnerabilities, m) - } else { + } + if misconfigsResource(m) { + res, ok := index[m.fullname()] index[m.fullname()] = m + if ok { + index[m.fullname()].Results[0].Misconfigurations = append(index[m.fullname()].Results[0].Misconfigurations, res.Results[0].Misconfigurations...) + } } } @@ -278,6 +283,10 @@ func vulnerabilitiesOrSecretResource(resource Resource) bool { return len(resource.Results) > 0 && (len(resource.Results[0].Vulnerabilities) > 0 || len(resource.Results[0].Secrets) > 0) } +func misconfigsResource(resource Resource) bool { + return len(resource.Results) > 0 && len(resource.Results[0].Misconfigurations) > 0 +} + func nodeKind(resource Resource) Resource { if nodeInfoResource(resource) { resource.Kind = "Node" diff --git a/pkg/k8s/report/report_test.go b/pkg/k8s/report/report_test.go index 1a984eb71b23..9ba663dc4783 100644 --- a/pkg/k8s/report/report_test.go +++ b/pkg/k8s/report/report_test.go @@ -119,6 +119,23 @@ var ( }, } + orionDeployWithAnotherMisconfig = Resource{ + Namespace: "default", + Kind: "Deploy", + Name: "orion", + Results: types.Results{ + { + Misconfigurations: []types.DetectedMisconfiguration{ + { + ID: "ID201", + Status: types.MisconfStatusFailure, + Severity: "HIGH", + }, + }, + }, + }, + } + image1WithVulns = Resource{ Namespace: "default", Kind: "Pod", @@ -424,6 +441,10 @@ var ( ) func TestReport_consolidate(t *testing.T) { + concatenatedResource := orionDeployWithAnotherMisconfig + concatenatedResource.Results[0].Misconfigurations = append(concatenatedResource.Results[0].Misconfigurations, + deployOrionWithMisconfigs.Results[0].Misconfigurations...) + tests := []struct { name string report Report @@ -471,6 +492,18 @@ func TestReport_consolidate(t *testing.T) { "default/cronjob/hello": cronjobHelloWithVulns, }, }, + { + name: "report with misconfigs in image and pod", + report: Report{ + Resources: []Resource{ + deployOrionWithMisconfigs, + orionDeployWithAnotherMisconfig, + }, + }, + expectedFindings: map[string]Resource{ + "default/deploy/orion": concatenatedResource, + }, + }, { name: "report with multi image pod containing vulnerabilities", report: Report{ From e872ec006c0745a5a142728af0096c6d6bb9ddf3 Mon Sep 17 00:00:00 2001 From: Rutam Prita Mishra Date: Thu, 31 Oct 2024 11:33:22 +0530 Subject: [PATCH 062/108] fix(go): Do not trim v prefix from versions in Go Mod Analyzer (#7733) Co-authored-by: DmitriyLewen --- docs/docs/supply-chain/vex/file.md | 8 +- .../testdata/fixtures/vex/file/openvex.json | 2 +- integration/testdata/gomod-skip.json.golden | 24 +- integration/testdata/gomod-vex.json.golden | 24 +- integration/testdata/gomod.json.golden | 30 +- pkg/dependency/id_test.go | 2 +- pkg/dependency/parser/golang/binary/parse.go | 3 + .../parser/golang/binary/parse_test.go | 10 +- pkg/dependency/parser/golang/mod/parse.go | 25 +- .../parser/golang/mod/parse_testcase.go | 48 +- pkg/dependency/parser/golang/sum/parse.go | 2 +- .../parser/golang/sum/parse_testcase.go | 704 +++++++++--------- .../language/golang/binary/binary_test.go | 2 +- pkg/fanal/analyzer/language/golang/mod/mod.go | 2 +- .../analyzer/language/golang/mod/mod_test.go | 12 +- pkg/purl/purl_test.go | 20 +- pkg/result/filter_test.go | 14 +- pkg/result/testdata/openvex.json | 2 +- pkg/sbom/io/encode_test.go | 32 +- pkg/vex/testdata/csaf-relationships.json | 4 +- pkg/vex/testdata/csaf.json | 2 +- pkg/vex/testdata/openvex-nested.json | 4 +- pkg/vex/vex_test.go | 40 +- 23 files changed, 510 insertions(+), 506 deletions(-) diff --git a/docs/docs/supply-chain/vex/file.md b/docs/docs/supply-chain/vex/file.md index 7c847ec49e14..242ec4c5a040 100644 --- a/docs/docs/supply-chain/vex/file.md +++ b/docs/docs/supply-chain/vex/file.md @@ -64,7 +64,7 @@ $ cat < trivy.vex.cdx }, "affects": [ { - "ref": "urn:cdx:3e671687-395b-41f5-a30f-a58921a69b79/1#pkg:golang/github.com/aws/aws-sdk-go@1.44.234" + "ref": "urn:cdx:3e671687-395b-41f5-a30f-a58921a69b79/1#pkg:golang/github.com/aws/aws-sdk-go@v1.44.234" } ] } @@ -115,7 +115,7 @@ Total: 1 (UNKNOWN: 0, LOW: 1, MEDIUM: 0, HIGH: 0, CRITICAL: 0) ┌───────────────────────────┬───────────────┬──────────┬───────────────────┬───────────────┬────────────────────────────────────────────────────────────┐ │ Library │ Vulnerability │ Severity │ Installed Version │ Fixed Version │ Title │ ├───────────────────────────┼───────────────┼──────────┼───────────────────┼───────────────┼────────────────────────────────────────────────────────────┤ -│ github.com/aws/aws-sdk-go │ CVE-2020-8912 │ LOW │ 1.44.234 │ │ aws-sdk-go: In-band key negotiation issue in AWS S3 Crypto │ +│ github.com/aws/aws-sdk-go │ CVE-2020-8912 │ LOW │ v1.44.234 │ │ aws-sdk-go: In-band key negotiation issue in AWS S3 Crypto │ │ │ │ │ │ │ SDK for golang... │ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2020-8912 │ └───────────────────────────┴───────────────┴──────────┴───────────────────┴───────────────┴────────────────────────────────────────────────────────────┘ @@ -497,9 +497,9 @@ Now, suppose a VEX statement is issued for `Module B` as follows: "vulnerability": {"name": "CVE-XXXX-YYYY"}, "products": [ { - "@id": "pkg:golang/module-b@1.0.0", + "@id": "pkg:golang/module-b@v1.0.0", "subcomponents": [ - { "@id": "pkg:golang/module-c@2.0.0" } + { "@id": "pkg:golang/module-c@v2.0.0" } ] } ], diff --git a/integration/testdata/fixtures/vex/file/openvex.json b/integration/testdata/fixtures/vex/file/openvex.json index 38773ba57694..9782e1716387 100644 --- a/integration/testdata/fixtures/vex/file/openvex.json +++ b/integration/testdata/fixtures/vex/file/openvex.json @@ -11,7 +11,7 @@ { "@id": "pkg:golang/github.com/testdata/testdata", "subcomponents": [ - { "@id": "pkg:golang/github.com/open-policy-agent/opa@0.35.0" } + { "@id": "pkg:golang/github.com/open-policy-agent/opa@v0.35.0" } ] } ], diff --git a/integration/testdata/gomod-skip.json.golden b/integration/testdata/gomod-skip.json.golden index 69ab998ec9dd..877fd627b89a 100644 --- a/integration/testdata/gomod-skip.json.golden +++ b/integration/testdata/gomod-skip.json.golden @@ -26,10 +26,10 @@ "PkgID": "github.com/docker/distribution@v2.7.1+incompatible", "PkgName": "github.com/docker/distribution", "PkgIdentifier": { - "PURL": "pkg:golang/github.com/docker/distribution@2.7.1%2Bincompatible", - "UID": "de19cd663ca047a8" + "PURL": "pkg:golang/github.com/docker/distribution@v2.7.1%2Bincompatible", + "UID": "9d949a7b01249e68" }, - "InstalledVersion": "2.7.1+incompatible", + "InstalledVersion": "v2.7.1+incompatible", "FixedVersion": "v2.8.0", "Status": "fixed", "Layer": {}, @@ -53,10 +53,10 @@ "PkgID": "github.com/open-policy-agent/opa@v0.35.0", "PkgName": "github.com/open-policy-agent/opa", "PkgIdentifier": { - "PURL": "pkg:golang/github.com/open-policy-agent/opa@0.35.0", - "UID": "6b685002e082ffc5" + "PURL": "pkg:golang/github.com/open-policy-agent/opa@v0.35.0", + "UID": "e89e2b0d8977e2a" }, - "InstalledVersion": "0.35.0", + "InstalledVersion": "v0.35.0", "FixedVersion": "0.37.0", "Status": "fixed", "Layer": {}, @@ -100,10 +100,10 @@ "PkgID": "golang.org/x/text@v0.3.6", "PkgName": "golang.org/x/text", "PkgIdentifier": { - "PURL": "pkg:golang/golang.org/x/text@0.3.6", - "UID": "825dc613c0f39d45" + "PURL": "pkg:golang/golang.org/x/text@v0.3.6", + "UID": "3050088ce9eb2ce4" }, - "InstalledVersion": "0.3.6", + "InstalledVersion": "v0.3.6", "FixedVersion": "0.3.7", "Status": "fixed", "Layer": {}, @@ -133,10 +133,10 @@ "PkgID": "github.com/docker/distribution@v2.7.1+incompatible", "PkgName": "github.com/docker/distribution", "PkgIdentifier": { - "PURL": "pkg:golang/github.com/docker/distribution@2.7.1%2Bincompatible", - "UID": "94376dc37054a7e8" + "PURL": "pkg:golang/github.com/docker/distribution@v2.7.1%2Bincompatible", + "UID": "2f7f0fa81860b8f1" }, - "InstalledVersion": "2.7.1+incompatible", + "InstalledVersion": "v2.7.1+incompatible", "FixedVersion": "v2.8.0", "Status": "fixed", "Layer": {}, diff --git a/integration/testdata/gomod-vex.json.golden b/integration/testdata/gomod-vex.json.golden index a2269bd1d0b7..34d96dc0ce95 100644 --- a/integration/testdata/gomod-vex.json.golden +++ b/integration/testdata/gomod-vex.json.golden @@ -26,10 +26,10 @@ "PkgID": "github.com/docker/distribution@v2.7.1+incompatible", "PkgName": "github.com/docker/distribution", "PkgIdentifier": { - "PURL": "pkg:golang/github.com/docker/distribution@2.7.1%2Bincompatible", - "UID": "de19cd663ca047a8" + "PURL": "pkg:golang/github.com/docker/distribution@v2.7.1%2Bincompatible", + "UID": "9d949a7b01249e68" }, - "InstalledVersion": "2.7.1+incompatible", + "InstalledVersion": "v2.7.1+incompatible", "FixedVersion": "v2.8.0", "Status": "fixed", "Layer": {}, @@ -53,10 +53,10 @@ "PkgID": "golang.org/x/text@v0.3.6", "PkgName": "golang.org/x/text", "PkgIdentifier": { - "PURL": "pkg:golang/golang.org/x/text@0.3.6", - "UID": "825dc613c0f39d45" + "PURL": "pkg:golang/golang.org/x/text@v0.3.6", + "UID": "3050088ce9eb2ce4" }, - "InstalledVersion": "0.3.6", + "InstalledVersion": "v0.3.6", "FixedVersion": "0.3.7", "Status": "fixed", "Layer": {}, @@ -86,10 +86,10 @@ "PkgID": "github.com/docker/distribution@v2.7.1+incompatible", "PkgName": "github.com/docker/distribution", "PkgIdentifier": { - "PURL": "pkg:golang/github.com/docker/distribution@2.7.1%2Bincompatible", - "UID": "94376dc37054a7e8" + "PURL": "pkg:golang/github.com/docker/distribution@v2.7.1%2Bincompatible", + "UID": "2f7f0fa81860b8f1" }, - "InstalledVersion": "2.7.1+incompatible", + "InstalledVersion": "v2.7.1+incompatible", "FixedVersion": "v2.8.0", "Status": "fixed", "Layer": {}, @@ -120,10 +120,10 @@ "PkgID": "github.com/docker/distribution@v2.7.1+incompatible", "PkgName": "github.com/docker/distribution", "PkgIdentifier": { - "PURL": "pkg:golang/github.com/docker/distribution@2.7.1%2Bincompatible", - "UID": "94306cdcf85fb50a" + "PURL": "pkg:golang/github.com/docker/distribution@v2.7.1%2Bincompatible", + "UID": "3ad40723ed2fce22" }, - "InstalledVersion": "2.7.1+incompatible", + "InstalledVersion": "v2.7.1+incompatible", "FixedVersion": "v2.8.0", "Status": "fixed", "Layer": {}, diff --git a/integration/testdata/gomod.json.golden b/integration/testdata/gomod.json.golden index 627088188285..551b787cadd6 100644 --- a/integration/testdata/gomod.json.golden +++ b/integration/testdata/gomod.json.golden @@ -26,10 +26,10 @@ "PkgID": "github.com/docker/distribution@v2.7.1+incompatible", "PkgName": "github.com/docker/distribution", "PkgIdentifier": { - "PURL": "pkg:golang/github.com/docker/distribution@2.7.1%2Bincompatible", - "UID": "de19cd663ca047a8" + "PURL": "pkg:golang/github.com/docker/distribution@v2.7.1%2Bincompatible", + "UID": "9d949a7b01249e68" }, - "InstalledVersion": "2.7.1+incompatible", + "InstalledVersion": "v2.7.1+incompatible", "FixedVersion": "v2.8.0", "Status": "fixed", "Layer": {}, @@ -53,10 +53,10 @@ "PkgID": "github.com/open-policy-agent/opa@v0.35.0", "PkgName": "github.com/open-policy-agent/opa", "PkgIdentifier": { - "PURL": "pkg:golang/github.com/open-policy-agent/opa@0.35.0", - "UID": "6b685002e082ffc5" + "PURL": "pkg:golang/github.com/open-policy-agent/opa@v0.35.0", + "UID": "e89e2b0d8977e2a" }, - "InstalledVersion": "0.35.0", + "InstalledVersion": "v0.35.0", "FixedVersion": "0.37.0", "Status": "fixed", "Layer": {}, @@ -100,10 +100,10 @@ "PkgID": "golang.org/x/text@v0.3.6", "PkgName": "golang.org/x/text", "PkgIdentifier": { - "PURL": "pkg:golang/golang.org/x/text@0.3.6", - "UID": "825dc613c0f39d45" + "PURL": "pkg:golang/golang.org/x/text@v0.3.6", + "UID": "3050088ce9eb2ce4" }, - "InstalledVersion": "0.3.6", + "InstalledVersion": "v0.3.6", "FixedVersion": "0.3.7", "Status": "fixed", "Layer": {}, @@ -133,10 +133,10 @@ "PkgID": "github.com/docker/distribution@v2.7.1+incompatible", "PkgName": "github.com/docker/distribution", "PkgIdentifier": { - "PURL": "pkg:golang/github.com/docker/distribution@2.7.1%2Bincompatible", - "UID": "94376dc37054a7e8" + "PURL": "pkg:golang/github.com/docker/distribution@v2.7.1%2Bincompatible", + "UID": "2f7f0fa81860b8f1" }, - "InstalledVersion": "2.7.1+incompatible", + "InstalledVersion": "v2.7.1+incompatible", "FixedVersion": "v2.8.0", "Status": "fixed", "Layer": {}, @@ -167,10 +167,10 @@ "PkgID": "github.com/docker/distribution@v2.7.1+incompatible", "PkgName": "github.com/docker/distribution", "PkgIdentifier": { - "PURL": "pkg:golang/github.com/docker/distribution@2.7.1%2Bincompatible", - "UID": "94306cdcf85fb50a" + "PURL": "pkg:golang/github.com/docker/distribution@v2.7.1%2Bincompatible", + "UID": "3ad40723ed2fce22" }, - "InstalledVersion": "2.7.1+incompatible", + "InstalledVersion": "v2.7.1+incompatible", "FixedVersion": "v2.8.0", "Status": "fixed", "Layer": {}, diff --git a/pkg/dependency/id_test.go b/pkg/dependency/id_test.go index 18359f771e7b..2b8c0e4a71b1 100644 --- a/pkg/dependency/id_test.go +++ b/pkg/dependency/id_test.go @@ -34,7 +34,7 @@ func TestID(t *testing.T) { args: args{ ltype: types.GoModule, name: "test", - version: "1.0.0", + version: "v1.0.0", }, want: "test@v1.0.0", }, diff --git a/pkg/dependency/parser/golang/binary/parse.go b/pkg/dependency/parser/golang/binary/parse.go index 6959e0ede5fa..2a7a2a128de7 100644 --- a/pkg/dependency/parser/golang/binary/parse.go +++ b/pkg/dependency/parser/golang/binary/parse.go @@ -3,6 +3,7 @@ package binary import ( "cmp" "debug/buildinfo" + "fmt" "runtime/debug" "slices" "sort" @@ -56,6 +57,8 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc // Ex: "go1.22.3 X:boringcrypto" stdlibVersion := strings.TrimPrefix(info.GoVersion, "go") stdlibVersion, _, _ = strings.Cut(stdlibVersion, " ") + // Add the `v` prefix to be consistent with module and dependency versions. + stdlibVersion = fmt.Sprintf("v%s", stdlibVersion) ldflags := p.ldFlags(info.Settings) pkgs := make(ftypes.Packages, 0, len(info.Deps)+2) diff --git a/pkg/dependency/parser/golang/binary/parse_test.go b/pkg/dependency/parser/golang/binary/parse_test.go index 2fbb6acff7b2..aade2a32cf24 100644 --- a/pkg/dependency/parser/golang/binary/parse_test.go +++ b/pkg/dependency/parser/golang/binary/parse_test.go @@ -20,7 +20,7 @@ func TestParse(t *testing.T) { }, { Name: "stdlib", - Version: "1.15.2", + Version: "v1.15.2", Relationship: ftypes.RelationshipDirect, }, { @@ -69,7 +69,7 @@ func TestParse(t *testing.T) { }, { Name: "stdlib", - Version: "1.16.4", + Version: "v1.16.4", Relationship: ftypes.RelationshipDirect, }, { @@ -93,7 +93,7 @@ func TestParse(t *testing.T) { }, { Name: "stdlib", - Version: "1.20.6", + Version: "v1.20.6", Relationship: ftypes.RelationshipDirect, }, }, @@ -109,7 +109,7 @@ func TestParse(t *testing.T) { }, { Name: "stdlib", - Version: "1.22.1", + Version: "v1.22.1", Relationship: ftypes.RelationshipDirect, }, }, @@ -120,7 +120,7 @@ func TestParse(t *testing.T) { want: []ftypes.Package{ { Name: "stdlib", - Version: "1.22.1", + Version: "v1.22.1", Relationship: ftypes.RelationshipDirect, }, }, diff --git a/pkg/dependency/parser/golang/mod/parse.go b/pkg/dependency/parser/golang/mod/parse.go index bbf42926a766..ddcd2ccc880e 100644 --- a/pkg/dependency/parser/golang/mod/parse.go +++ b/pkg/dependency/parser/golang/mod/parse.go @@ -1,6 +1,7 @@ package mod import ( + "fmt" "io" "regexp" "strconv" @@ -90,9 +91,11 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc if p.useMinVersion { if toolchainVer := toolchainVersion(modFileParsed.Toolchain, modFileParsed.Go); toolchainVer != "" { pkgs["stdlib"] = ftypes.Package{ - ID: packageID("stdlib", toolchainVer), - Name: "stdlib", - Version: toolchainVer, + ID: packageID("stdlib", toolchainVer), + Name: "stdlib", + // Our versioning library doesn't support canonical (goX.Y.Z) format, + // So we need to add `v` prefix for consistency (with module and dependency versions). + Version: fmt.Sprintf("v%s", toolchainVer), Relationship: ftypes.RelationshipDirect, // Considered a direct dependency as the main module depends on the standard packages. } } @@ -100,11 +103,10 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc // Main module if m := modFileParsed.Module; m != nil { - ver := strings.TrimPrefix(m.Mod.Version, "v") pkgs[m.Mod.Path] = ftypes.Package{ - ID: packageID(m.Mod.Path, ver), + ID: packageID(m.Mod.Path, m.Mod.Version), Name: m.Mod.Path, - Version: ver, + Version: m.Mod.Version, ExternalReferences: p.GetExternalRefs(m.Mod.Path), Relationship: ftypes.RelationshipRoot, } @@ -116,11 +118,10 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc if skipIndirect && require.Indirect { continue } - ver := strings.TrimPrefix(require.Mod.Version, "v") pkgs[require.Mod.Path] = ftypes.Package{ - ID: packageID(require.Mod.Path, ver), + ID: packageID(require.Mod.Path, require.Mod.Version), Name: require.Mod.Path, - Version: ver, + Version: require.Mod.Version, Relationship: lo.Ternary(require.Indirect, ftypes.RelationshipIndirect, ftypes.RelationshipDirect), ExternalReferences: p.GetExternalRefs(require.Mod.Path), } @@ -136,7 +137,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc } // If the replace directive has a version on the left side, make sure it matches the version that was imported. - if rep.Old.Version != "" && old.Version != rep.Old.Version[1:] { + if rep.Old.Version != "" && old.Version != rep.Old.Version { continue } @@ -153,9 +154,9 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc // Add replaced package to package register. pkgs[rep.New.Path] = ftypes.Package{ - ID: packageID(rep.New.Path, rep.New.Version[1:]), + ID: packageID(rep.New.Path, rep.New.Version), Name: rep.New.Path, - Version: rep.New.Version[1:], + Version: rep.New.Version, Relationship: old.Relationship, ExternalReferences: p.GetExternalRefs(rep.New.Path), } diff --git a/pkg/dependency/parser/golang/mod/parse_testcase.go b/pkg/dependency/parser/golang/mod/parse_testcase.go index 4671ef3e6854..b8ed49008926 100644 --- a/pkg/dependency/parser/golang/mod/parse_testcase.go +++ b/pkg/dependency/parser/golang/mod/parse_testcase.go @@ -23,13 +23,13 @@ var ( { ID: "stdlib@v1.22.5", Name: "stdlib", - Version: "1.22.5", + Version: "v1.22.5", Relationship: ftypes.RelationshipDirect, }, { ID: "github.com/aquasecurity/go-version@v0.0.0-20240603093900-cf8a8d29271d", Name: "github.com/aquasecurity/go-version", - Version: "0.0.0-20240603093900-cf8a8d29271d", + Version: "v0.0.0-20240603093900-cf8a8d29271d", Relationship: ftypes.RelationshipDirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -41,7 +41,7 @@ var ( { ID: "github.com/davecgh/go-spew@v1.1.2-0.20180830191138-d8f796af33cc", Name: "github.com/davecgh/go-spew", - Version: "1.1.2-0.20180830191138-d8f796af33cc", + Version: "v1.1.2-0.20180830191138-d8f796af33cc", Relationship: ftypes.RelationshipIndirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -53,7 +53,7 @@ var ( { ID: "github.com/pmezard/go-difflib@v1.0.1-0.20181226105442-5d4384ee4fb2", Name: "github.com/pmezard/go-difflib", - Version: "1.0.1-0.20181226105442-5d4384ee4fb2", + Version: "v1.0.1-0.20181226105442-5d4384ee4fb2", Relationship: ftypes.RelationshipIndirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -65,7 +65,7 @@ var ( { ID: "github.com/stretchr/testify@v1.9.0", Name: "github.com/stretchr/testify", - Version: "1.9.0", + Version: "v1.9.0", Relationship: ftypes.RelationshipIndirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -77,7 +77,7 @@ var ( { ID: "golang.org/x/xerrors@v0.0.0-20231012003039-104605ab7028", Name: "golang.org/x/xerrors", - Version: "0.0.0-20231012003039-104605ab7028", + Version: "v0.0.0-20231012003039-104605ab7028", Relationship: ftypes.RelationshipIndirect, }, } @@ -102,7 +102,7 @@ var ( { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20220406074731-71021a481237", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20220406074731-71021a481237", + Version: "v0.0.0-20220406074731-71021a481237", Relationship: ftypes.RelationshipDirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -114,7 +114,7 @@ var ( { ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1", Name: "golang.org/x/xerrors", - Version: "0.0.0-20200804184101-5ec99f83aff1", + Version: "v0.0.0-20200804184101-5ec99f83aff1", Relationship: ftypes.RelationshipIndirect, }, } @@ -135,7 +135,7 @@ var ( { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211110174639-8257534ffed3", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20211110174639-8257534ffed3", + Version: "v0.0.0-20211110174639-8257534ffed3", Relationship: ftypes.RelationshipDirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -147,7 +147,7 @@ var ( { ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1", Name: "golang.org/x/xerrors", - Version: "0.0.0-20200804184101-5ec99f83aff1", + Version: "v0.0.0-20200804184101-5ec99f83aff1", Relationship: ftypes.RelationshipIndirect, }, } @@ -168,7 +168,7 @@ var ( { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20220406074731-71021a481237", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20220406074731-71021a481237", + Version: "v0.0.0-20220406074731-71021a481237", Relationship: ftypes.RelationshipDirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -180,7 +180,7 @@ var ( { ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1", Name: "golang.org/x/xerrors", - Version: "0.0.0-20200804184101-5ec99f83aff1", + Version: "v0.0.0-20200804184101-5ec99f83aff1", Relationship: ftypes.RelationshipIndirect, }, } @@ -201,7 +201,7 @@ var ( { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211224170007-df43bca6b6ff", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20211224170007-df43bca6b6ff", + Version: "v0.0.0-20211224170007-df43bca6b6ff", Relationship: ftypes.RelationshipDirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -213,13 +213,13 @@ var ( { ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1", Name: "golang.org/x/xerrors", - Version: "0.0.0-20200804184101-5ec99f83aff1", + Version: "v0.0.0-20200804184101-5ec99f83aff1", Relationship: ftypes.RelationshipIndirect, }, { ID: "gopkg.in/yaml.v3@v3.0.0-20210107192922-496545a6307b", Name: "gopkg.in/yaml.v3", - Version: "3.0.0-20210107192922-496545a6307b", + Version: "v3.0.0-20210107192922-496545a6307b", Relationship: ftypes.RelationshipIndirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -246,7 +246,7 @@ var ( { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211224170007-df43bca6b6ff", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20211224170007-df43bca6b6ff", + Version: "v0.0.0-20211224170007-df43bca6b6ff", Relationship: ftypes.RelationshipDirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -258,7 +258,7 @@ var ( { ID: "gopkg.in/yaml.v3@v3.0.0-20210107192922-496545a6307b", Name: "gopkg.in/yaml.v3", - Version: "3.0.0-20210107192922-496545a6307b", + Version: "v3.0.0-20210107192922-496545a6307b", Relationship: ftypes.RelationshipIndirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -285,7 +285,7 @@ var ( { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211224170007-df43bca6b6ff", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20211224170007-df43bca6b6ff", + Version: "v0.0.0-20211224170007-df43bca6b6ff", Relationship: ftypes.RelationshipDirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -297,7 +297,7 @@ var ( { ID: "gopkg.in/yaml.v3@v3.0.0-20210107192922-496545a6307b", Name: "gopkg.in/yaml.v3", - Version: "3.0.0-20210107192922-496545a6307b", + Version: "v3.0.0-20210107192922-496545a6307b", Relationship: ftypes.RelationshipIndirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -324,7 +324,7 @@ var ( { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211224170007-df43bca6b6ff", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20211224170007-df43bca6b6ff", + Version: "v0.0.0-20211224170007-df43bca6b6ff", Relationship: ftypes.RelationshipDirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -336,13 +336,13 @@ var ( { ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1", Name: "golang.org/x/xerrors", - Version: "0.0.0-20200804184101-5ec99f83aff1", + Version: "v0.0.0-20200804184101-5ec99f83aff1", Relationship: ftypes.RelationshipIndirect, }, { ID: "gopkg.in/yaml.v3@v3.0.0-20210107192922-496545a6307b", Name: "gopkg.in/yaml.v3", - Version: "3.0.0-20210107192922-496545a6307b", + Version: "v3.0.0-20210107192922-496545a6307b", Relationship: ftypes.RelationshipIndirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -369,7 +369,7 @@ var ( { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211224170007-df43bca6b6ff", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20211224170007-df43bca6b6ff", + Version: "v0.0.0-20211224170007-df43bca6b6ff", Relationship: ftypes.RelationshipDirect, ExternalReferences: []ftypes.ExternalRef{ { @@ -396,7 +396,7 @@ var ( { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20211224170007-df43bca6b6ff", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20211224170007-df43bca6b6ff", + Version: "v0.0.0-20211224170007-df43bca6b6ff", Relationship: ftypes.RelationshipDirect, ExternalReferences: []ftypes.ExternalRef{ { diff --git a/pkg/dependency/parser/golang/sum/parse.go b/pkg/dependency/parser/golang/sum/parse.go index 4ec742b1bae2..8362a808c036 100644 --- a/pkg/dependency/parser/golang/sum/parse.go +++ b/pkg/dependency/parser/golang/sum/parse.go @@ -32,7 +32,7 @@ func (p *Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependenc // go.sum records and sorts all non-major versions // with the latest version as last entry - uniquePkgs[s[0]] = strings.TrimSuffix(strings.TrimPrefix(s[1], "v"), "/go.mod") + uniquePkgs[s[0]] = strings.TrimSuffix(s[1], "/go.mod") } if err := scanner.Err(); err != nil { return nil, nil, xerrors.Errorf("scan error: %w", err) diff --git a/pkg/dependency/parser/golang/sum/parse_testcase.go b/pkg/dependency/parser/golang/sum/parse_testcase.go index fc607de86e2a..532a241b1ccc 100644 --- a/pkg/dependency/parser/golang/sum/parse_testcase.go +++ b/pkg/dependency/parser/golang/sum/parse_testcase.go @@ -11,13 +11,13 @@ var ( // go get golang.org/x/xerrors // go list -m all | awk 'NR>1 {sub(/^v/, "", $2); printf("{\""$1"\", \""$2"\", },\n")}' GoModNormal = []ftypes.Package{ - {Name: "golang.org/x/xerrors", Version: "0.0.0-20200804184101-5ec99f83aff1"}, + {Name: "golang.org/x/xerrors", Version: "v0.0.0-20200804184101-5ec99f83aff1"}, } // https://github.com/uudashr/gopkgs/blob/616744904701ef01d868da4b66aad0e6856c361d/v2/go.sum GoModEmptyLine = []ftypes.Package{ - {Name: "github.com/karrick/godirwalk", Version: "1.12.0"}, - {Name: "github.com/pkg/errors", Version: "0.8.1"}, + {Name: "github.com/karrick/godirwalk", Version: "v1.12.0"}, + {Name: "github.com/pkg/errors", Version: "v0.8.1"}, } // docker run --name gomod --rm -it golang:1.15 bash @@ -31,19 +31,19 @@ var ( // go get github.com/BurntSushi/toml // go list -m all | awk 'NR>1 {sub(/^v/, "", $2); printf("{\""$1"\", \""$2"\", },\n")}' GoModMany = []ftypes.Package{ - {Name: "github.com/BurntSushi/toml", Version: "0.3.1"}, - {Name: "github.com/cpuguy83/go-md2man/v2", Version: "2.0.0-20190314233015-f79a8a8ca69d"}, - {Name: "github.com/davecgh/go-spew", Version: "1.1.0"}, - {Name: "github.com/pmezard/go-difflib", Version: "1.0.0"}, - {Name: "github.com/russross/blackfriday/v2", Version: "2.0.1"}, - {Name: "github.com/shurcooL/sanitized_anchor_name", Version: "1.0.0"}, - {Name: "github.com/stretchr/objx", Version: "0.1.0"}, - {Name: "github.com/stretchr/testify", Version: "1.7.0"}, - {Name: "github.com/urfave/cli", Version: "1.22.5"}, - {Name: "golang.org/x/xerrors", Version: "0.0.0-20200804184101-5ec99f83aff1"}, - {Name: "gopkg.in/check.v1", Version: "0.0.0-20161208181325-20d25e280405"}, - {Name: "gopkg.in/yaml.v2", Version: "2.2.2"}, - {Name: "gopkg.in/yaml.v3", Version: "3.0.0-20200313102051-9f266ea9e77c"}, + {Name: "github.com/BurntSushi/toml", Version: "v0.3.1"}, + {Name: "github.com/cpuguy83/go-md2man/v2", Version: "v2.0.0-20190314233015-f79a8a8ca69d"}, + {Name: "github.com/davecgh/go-spew", Version: "v1.1.0"}, + {Name: "github.com/pmezard/go-difflib", Version: "v1.0.0"}, + {Name: "github.com/russross/blackfriday/v2", Version: "v2.0.1"}, + {Name: "github.com/shurcooL/sanitized_anchor_name", Version: "v1.0.0"}, + {Name: "github.com/stretchr/objx", Version: "v0.1.0"}, + {Name: "github.com/stretchr/testify", Version: "v1.7.0"}, + {Name: "github.com/urfave/cli", Version: "v1.22.5"}, + {Name: "golang.org/x/xerrors", Version: "v0.0.0-20200804184101-5ec99f83aff1"}, + {Name: "gopkg.in/check.v1", Version: "v0.0.0-20161208181325-20d25e280405"}, + {Name: "gopkg.in/yaml.v2", Version: "v2.2.2"}, + {Name: "gopkg.in/yaml.v3", Version: "v3.0.0-20200313102051-9f266ea9e77c"}, } // docker run --name gomod --rm -it golang:1.15 bash @@ -54,341 +54,341 @@ var ( // go get github.com/aquasecurity/trivy // go list -m all | awk 'NR>1 {sub(/^v/, "", $2); printf("{\""$1"\", \""$2"\", },\n")}' GoModTrivy = []ftypes.Package{ - {Name: "cloud.google.com/go", Version: "0.65.0"}, - {Name: "cloud.google.com/go/bigquery", Version: "1.8.0"}, - {Name: "cloud.google.com/go/datastore", Version: "1.1.0"}, - {Name: "cloud.google.com/go/pubsub", Version: "1.3.1"}, - {Name: "cloud.google.com/go/storage", Version: "1.10.0"}, - {Name: "dmitri.shuralyov.com/gpu/mtl", Version: "0.0.0-20190408044501-666a987793e9"}, - {Name: "github.com/Azure/azure-sdk-for-go", Version: "38.0.0+incompatible"}, - {Name: "github.com/Azure/go-ansiterm", Version: "0.0.0-20170929234023-d6e3b3328b78"}, - {Name: "github.com/Azure/go-autorest/autorest", Version: "0.9.3"}, - {Name: "github.com/Azure/go-autorest/autorest/adal", Version: "0.8.1"}, - {Name: "github.com/Azure/go-autorest/autorest/date", Version: "0.2.0"}, - {Name: "github.com/Azure/go-autorest/autorest/mocks", Version: "0.3.0"}, - {Name: "github.com/Azure/go-autorest/autorest/to", Version: "0.3.0"}, - {Name: "github.com/Azure/go-autorest/autorest/validation", Version: "0.1.0"}, - {Name: "github.com/Azure/go-autorest/logger", Version: "0.1.0"}, - {Name: "github.com/Azure/go-autorest/tracing", Version: "0.5.0"}, - {Name: "github.com/BurntSushi/toml", Version: "0.3.1"}, - {Name: "github.com/BurntSushi/xgb", Version: "0.0.0-20160522181843-27f122750802"}, - {Name: "github.com/GoogleCloudPlatform/docker-credential-gcr", Version: "1.5.0"}, - {Name: "github.com/GoogleCloudPlatform/k8s-cloud-provider", Version: "0.0.0-20190822182118-27a4ced34534"}, - {Name: "github.com/Microsoft/go-winio", Version: "0.4.15-0.20190919025122-fc70bd9a86b5"}, - {Name: "github.com/Microsoft/hcsshim", Version: "0.8.6"}, - {Name: "github.com/NYTimes/gziphandler", Version: "0.0.0-20170623195520-56545f4a5d46"}, - {Name: "github.com/OneOfOne/xxhash", Version: "1.2.7"}, - {Name: "github.com/PuerkitoBio/purell", Version: "1.1.1"}, - {Name: "github.com/PuerkitoBio/urlesc", Version: "0.0.0-20170810143723-de5bf2ad4578"}, - {Name: "github.com/VividCortex/ewma", Version: "1.1.1"}, - {Name: "github.com/alcortesm/tgz", Version: "0.0.0-20161220082320-9c5fe88206d7"}, - {Name: "github.com/alecthomas/template", Version: "0.0.0-20160405071501-a0175ee3bccc"}, - {Name: "github.com/alecthomas/units", Version: "0.0.0-20151022065526-2efee857e7cf"}, - {Name: "github.com/alicebob/gopher-json", Version: "0.0.0-20200520072559-a9ecdc9d1d3a"}, - {Name: "github.com/alicebob/miniredis/v2", Version: "2.14.1"}, - {Name: "github.com/anmitsu/go-shlex", Version: "0.0.0-20161002113705-648efa622239"}, - {Name: "github.com/aquasecurity/bolt-fixtures", Version: "0.0.0-20200903104109-d34e7f983986"}, - {Name: "github.com/aquasecurity/fanal", Version: "0.0.0-20210119051230-28c249da7cfd"}, - {Name: "github.com/aquasecurity/go-dep-parser", Version: "0.0.0-20201028043324-889d4a92b8e0"}, - {Name: "github.com/aquasecurity/go-gem-version", Version: "0.0.0-20201115065557-8eed6fe000ce"}, - {Name: "github.com/aquasecurity/go-npm-version", Version: "0.0.0-20201110091526-0b796d180798"}, - {Name: "github.com/aquasecurity/go-pep440-version", Version: "0.0.0-20210121094942-22b2f8951d46"}, - {Name: "github.com/aquasecurity/go-version", Version: "0.0.0-20210121072130-637058cfe492"}, - {Name: "github.com/aquasecurity/testdocker", Version: "0.0.0-20210106133225-0b17fe083674"}, - {Name: "github.com/aquasecurity/trivy", Version: "0.16.0"}, - {Name: "github.com/aquasecurity/trivy-db", Version: "0.0.0-20210105160501-c5bf4e153277"}, - {Name: "github.com/aquasecurity/vuln-list-update", Version: "0.0.0-20191016075347-3d158c2bf9a2"}, - {Name: "github.com/araddon/dateparse", Version: "0.0.0-20190426192744-0d74ffceef83"}, - {Name: "github.com/armon/consul-api", Version: "0.0.0-20180202201655-eb2c6b5be1b6"}, - {Name: "github.com/armon/go-socks5", Version: "0.0.0-20160902184237-e75332964ef5"}, - {Name: "github.com/aws/aws-sdk-go", Version: "1.27.1"}, - {Name: "github.com/beorn7/perks", Version: "1.0.0"}, - {Name: "github.com/bgentry/speakeasy", Version: "0.1.0"}, - {Name: "github.com/blang/semver", Version: "3.5.0+incompatible"}, - {Name: "github.com/briandowns/spinner", Version: "1.12.0"}, - {Name: "github.com/caarlos0/env/v6", Version: "6.0.0"}, - {Name: "github.com/cenkalti/backoff", Version: "2.2.1+incompatible"}, - {Name: "github.com/census-instrumentation/opencensus-proto", Version: "0.2.1"}, - {Name: "github.com/cespare/xxhash/v2", Version: "2.1.1"}, - {Name: "github.com/cheggaaa/pb/v3", Version: "3.0.3"}, - {Name: "github.com/chzyer/logex", Version: "1.1.10"}, - {Name: "github.com/chzyer/readline", Version: "0.0.0-20180603132655-2972be24d48e"}, - {Name: "github.com/chzyer/test", Version: "0.0.0-20180213035817-a1ea475d72b1"}, - {Name: "github.com/client9/misspell", Version: "0.3.4"}, - {Name: "github.com/cncf/udpa/go", Version: "0.0.0-20191209042840-269d4d468f6f"}, - {Name: "github.com/cockroachdb/datadriven", Version: "0.0.0-20190809214429-80d97fb3cbaa"}, - {Name: "github.com/containerd/containerd", Version: "1.3.3"}, - {Name: "github.com/containerd/continuity", Version: "0.0.0-20190426062206-aaeac12a7ffc"}, - {Name: "github.com/coreos/etcd", Version: "3.3.10+incompatible"}, - {Name: "github.com/coreos/go-etcd", Version: "2.0.0+incompatible"}, - {Name: "github.com/coreos/go-oidc", Version: "2.1.0+incompatible"}, - {Name: "github.com/coreos/go-semver", Version: "0.3.0"}, - {Name: "github.com/coreos/go-systemd", Version: "0.0.0-20190321100706-95778dfbb74e"}, - {Name: "github.com/coreos/pkg", Version: "0.0.0-20180108230652-97fdf19511ea"}, - {Name: "github.com/cpuguy83/go-md2man", Version: "1.0.10"}, - {Name: "github.com/cpuguy83/go-md2man/v2", Version: "2.0.0"}, - {Name: "github.com/creack/pty", Version: "1.1.9"}, - {Name: "github.com/davecgh/go-spew", Version: "1.1.1"}, - {Name: "github.com/deckarep/golang-set", Version: "1.7.1"}, - {Name: "github.com/dgrijalva/jwt-go", Version: "3.2.0+incompatible"}, - {Name: "github.com/dgryski/go-rendezvous", Version: "0.0.0-20200823014737-9f7001d12a5f"}, - {Name: "github.com/dnaeon/go-vcr", Version: "1.0.1"}, - {Name: "github.com/docker/cli", Version: "0.0.0-20191017083524-a8ff7f821017"}, - {Name: "github.com/docker/distribution", Version: "2.7.1+incompatible"}, - {Name: "github.com/docker/docker", Version: "1.4.2-0.20190924003213-a8608b5b67c7"}, - {Name: "github.com/docker/docker-credential-helpers", Version: "0.6.3"}, - {Name: "github.com/docker/go-connections", Version: "0.4.0"}, - {Name: "github.com/docker/go-units", Version: "0.4.0"}, - {Name: "github.com/docker/spdystream", Version: "0.0.0-20160310174837-449fdfce4d96"}, - {Name: "github.com/dustin/go-humanize", Version: "1.0.0"}, - {Name: "github.com/elazarl/goproxy", Version: "0.0.0-20200809112317-0581fc3aee2d"}, - {Name: "github.com/elazarl/goproxy/ext", Version: "0.0.0-20200809112317-0581fc3aee2d"}, - {Name: "github.com/emicklei/go-restful", Version: "2.9.5+incompatible"}, - {Name: "github.com/emirpasic/gods", Version: "1.12.0"}, - {Name: "github.com/envoyproxy/go-control-plane", Version: "0.9.4"}, - {Name: "github.com/envoyproxy/protoc-gen-validate", Version: "0.1.0"}, - {Name: "github.com/evanphx/json-patch", Version: "4.2.0+incompatible"}, - {Name: "github.com/fatih/color", Version: "1.10.0"}, - {Name: "github.com/flynn/go-shlex", Version: "0.0.0-20150515145356-3f9db97f8568"}, - {Name: "github.com/fsnotify/fsnotify", Version: "1.4.9"}, - {Name: "github.com/ghodss/yaml", Version: "1.0.0"}, - {Name: "github.com/gin-contrib/sse", Version: "0.1.0"}, - {Name: "github.com/gin-gonic/gin", Version: "1.5.0"}, - {Name: "github.com/gliderlabs/ssh", Version: "0.2.2"}, - {Name: "github.com/go-git/gcfg", Version: "1.5.0"}, - {Name: "github.com/go-git/go-billy/v5", Version: "5.0.0"}, - {Name: "github.com/go-git/go-git-fixtures/v4", Version: "4.0.1"}, - {Name: "github.com/go-git/go-git/v5", Version: "5.0.0"}, - {Name: "github.com/go-gl/glfw", Version: "0.0.0-20190409004039-e6da0acd62b1"}, - {Name: "github.com/go-gl/glfw/v3.3/glfw", Version: "0.0.0-20200222043503-6f7a984d4dc4"}, - {Name: "github.com/go-kit/kit", Version: "0.8.0"}, - {Name: "github.com/go-logfmt/logfmt", Version: "0.3.0"}, - {Name: "github.com/go-logr/logr", Version: "0.1.0"}, - {Name: "github.com/go-openapi/jsonpointer", Version: "0.19.3"}, - {Name: "github.com/go-openapi/jsonreference", Version: "0.19.3"}, - {Name: "github.com/go-openapi/spec", Version: "0.19.3"}, - {Name: "github.com/go-openapi/swag", Version: "0.19.5"}, - {Name: "github.com/go-playground/locales", Version: "0.13.0"}, - {Name: "github.com/go-playground/universal-translator", Version: "0.17.0"}, - {Name: "github.com/go-redis/redis", Version: "6.15.7+incompatible"}, - {Name: "github.com/go-redis/redis/v8", Version: "8.4.0"}, - {Name: "github.com/go-restruct/restruct", Version: "0.0.0-20191227155143-5734170a48a1"}, - {Name: "github.com/go-sql-driver/mysql", Version: "1.5.0"}, - {Name: "github.com/go-stack/stack", Version: "1.8.0"}, - {Name: "github.com/gobwas/glob", Version: "0.2.3"}, - {Name: "github.com/goccy/go-yaml", Version: "1.8.2"}, - {Name: "github.com/gogo/protobuf", Version: "1.3.1"}, - {Name: "github.com/golang/glog", Version: "0.0.0-20160126235308-23def4e6c14b"}, - {Name: "github.com/golang/groupcache", Version: "0.0.0-20200121045136-8c9f03a8e57e"}, - {Name: "github.com/golang/mock", Version: "1.4.4"}, - {Name: "github.com/golang/protobuf", Version: "1.4.2"}, - {Name: "github.com/google/btree", Version: "1.0.0"}, - {Name: "github.com/google/go-cmp", Version: "0.5.3"}, - {Name: "github.com/google/go-containerregistry", Version: "0.0.0-20200331213917-3d03ed9b1ca2"}, - {Name: "github.com/google/go-github/v28", Version: "28.1.1"}, - {Name: "github.com/google/go-querystring", Version: "1.0.0"}, - {Name: "github.com/google/gofuzz", Version: "1.0.0"}, - {Name: "github.com/google/martian", Version: "2.1.0+incompatible"}, - {Name: "github.com/google/martian/v3", Version: "3.0.0"}, - {Name: "github.com/google/pprof", Version: "0.0.0-20200708004538-1a94d8640e99"}, - {Name: "github.com/google/renameio", Version: "0.1.0"}, - {Name: "github.com/google/subcommands", Version: "1.0.1"}, - {Name: "github.com/google/uuid", Version: "1.1.1"}, - {Name: "github.com/google/wire", Version: "0.3.0"}, - {Name: "github.com/googleapis/gax-go/v2", Version: "2.0.5"}, - {Name: "github.com/googleapis/gnostic", Version: "0.2.2"}, - {Name: "github.com/gophercloud/gophercloud", Version: "0.1.0"}, - {Name: "github.com/gopherjs/gopherjs", Version: "0.0.0-20200217142428-fce0ec30dd00"}, - {Name: "github.com/gorilla/context", Version: "1.1.1"}, - {Name: "github.com/gorilla/mux", Version: "1.7.4"}, - {Name: "github.com/gorilla/websocket", Version: "1.4.0"}, - {Name: "github.com/gregjones/httpcache", Version: "0.0.0-20180305231024-9cad4c3443a7"}, - {Name: "github.com/grpc-ecosystem/go-grpc-middleware", Version: "1.0.1-0.20190118093823-f849b5445de4"}, - {Name: "github.com/grpc-ecosystem/go-grpc-prometheus", Version: "1.2.0"}, - {Name: "github.com/grpc-ecosystem/grpc-gateway", Version: "1.9.5"}, - {Name: "github.com/hashicorp/errwrap", Version: "1.0.0"}, - {Name: "github.com/hashicorp/go-multierror", Version: "1.1.0"}, - {Name: "github.com/hashicorp/go-version", Version: "1.2.1"}, - {Name: "github.com/hashicorp/golang-lru", Version: "0.5.3"}, - {Name: "github.com/hashicorp/hcl", Version: "1.0.0"}, - {Name: "github.com/hpcloud/tail", Version: "1.0.0"}, - {Name: "github.com/ianlancetaylor/demangle", Version: "0.0.0-20181102032728-5e5cf60278f6"}, - {Name: "github.com/imdario/mergo", Version: "0.3.5"}, - {Name: "github.com/inconshreveable/mousetrap", Version: "1.0.0"}, - {Name: "github.com/jbenet/go-context", Version: "0.0.0-20150711004518-d14ea06fba99"}, - {Name: "github.com/jessevdk/go-flags", Version: "1.4.0"}, - {Name: "github.com/jmespath/go-jmespath", Version: "0.0.0-20180206201540-c2b33e8439af"}, - {Name: "github.com/joefitzgerald/rainbow-reporter", Version: "0.1.0"}, - {Name: "github.com/jonboulle/clockwork", Version: "0.1.0"}, - {Name: "github.com/json-iterator/go", Version: "1.1.8"}, - {Name: "github.com/jstemmer/go-junit-report", Version: "0.9.1"}, - {Name: "github.com/jtolds/gls", Version: "4.20.0+incompatible"}, - {Name: "github.com/julienschmidt/httprouter", Version: "1.2.0"}, - {Name: "github.com/kevinburke/ssh_config", Version: "0.0.0-20190725054713-01f96b0aa0cd"}, - {Name: "github.com/kisielk/errcheck", Version: "1.2.0"}, - {Name: "github.com/kisielk/gotool", Version: "1.0.0"}, - {Name: "github.com/knqyf263/go-apk-version", Version: "0.0.0-20200609155635-041fdbb8563f"}, - {Name: "github.com/knqyf263/go-deb-version", Version: "0.0.0-20190517075300-09fca494f03d"}, - {Name: "github.com/knqyf263/go-rpm-version", Version: "0.0.0-20170716094938-74609b86c936"}, - {Name: "github.com/knqyf263/go-rpmdb", Version: "0.0.0-20201215100354-a9e3110d8ee1"}, - {Name: "github.com/knqyf263/nested", Version: "0.0.1"}, - {Name: "github.com/konsorten/go-windows-terminal-sequences", Version: "1.0.2"}, - {Name: "github.com/kr/logfmt", Version: "0.0.0-20140226030751-b84e30acd515"}, - {Name: "github.com/kr/pretty", Version: "0.1.0"}, - {Name: "github.com/kr/pty", Version: "1.1.5"}, - {Name: "github.com/kr/text", Version: "0.2.0"}, - {Name: "github.com/kylelemons/godebug", Version: "1.1.0"}, - {Name: "github.com/leodido/go-urn", Version: "1.2.0"}, - {Name: "github.com/magiconair/properties", Version: "1.8.0"}, - {Name: "github.com/mailru/easyjson", Version: "0.7.0"}, - {Name: "github.com/mattn/go-colorable", Version: "0.1.8"}, - {Name: "github.com/mattn/go-isatty", Version: "0.0.12"}, - {Name: "github.com/mattn/go-jsonpointer", Version: "0.0.0-20180225143300-37667080efed"}, - {Name: "github.com/mattn/go-runewidth", Version: "0.0.9"}, - {Name: "github.com/matttproud/golang_protobuf_extensions", Version: "1.0.1"}, - {Name: "github.com/maxbrunsfeld/counterfeiter/v6", Version: "6.2.2"}, - {Name: "github.com/mitchellh/go-homedir", Version: "1.1.0"}, - {Name: "github.com/mitchellh/mapstructure", Version: "1.1.2"}, - {Name: "github.com/modern-go/concurrent", Version: "0.0.0-20180306012644-bacd9c7ef1dd"}, - {Name: "github.com/modern-go/reflect2", Version: "1.0.1"}, - {Name: "github.com/morikuni/aec", Version: "1.0.0"}, - {Name: "github.com/munnerz/goautoneg", Version: "0.0.0-20191010083416-a7dc8b61c822"}, - {Name: "github.com/mwitkow/go-conntrack", Version: "0.0.0-20161129095857-cc309e4a2223"}, - {Name: "github.com/mxk/go-flowrate", Version: "0.0.0-20140419014527-cca7078d478f"}, - {Name: "github.com/niemeyer/pretty", Version: "0.0.0-20200227124842-a10e7caefd8e"}, - {Name: "github.com/nxadm/tail", Version: "1.4.4"}, - {Name: "github.com/olekukonko/tablewriter", Version: "0.0.2-0.20190607075207-195002e6e56a"}, - {Name: "github.com/onsi/ginkgo", Version: "1.14.2"}, - {Name: "github.com/onsi/gomega", Version: "1.10.3"}, - {Name: "github.com/open-policy-agent/opa", Version: "0.21.1"}, - {Name: "github.com/opencontainers/go-digest", Version: "1.0.0-rc1"}, - {Name: "github.com/opencontainers/image-spec", Version: "1.0.2-0.20190823105129-775207bd45b6"}, - {Name: "github.com/opencontainers/runc", Version: "0.1.1"}, - {Name: "github.com/parnurzeal/gorequest", Version: "0.2.16"}, - {Name: "github.com/pelletier/go-toml", Version: "1.2.0"}, - {Name: "github.com/peterbourgon/diskv", Version: "2.0.1+incompatible"}, - {Name: "github.com/peterh/liner", Version: "0.0.0-20170211195444-bf27d3ba8e1d"}, - {Name: "github.com/pkg/errors", Version: "0.9.1"}, - {Name: "github.com/pmezard/go-difflib", Version: "1.0.0"}, - {Name: "github.com/pquerna/cachecontrol", Version: "0.0.0-20171018203845-0dec1b30a021"}, - {Name: "github.com/prometheus/client_golang", Version: "1.0.0"}, - {Name: "github.com/prometheus/client_model", Version: "0.0.0-20190812154241-14fe0d1b01d4"}, - {Name: "github.com/prometheus/common", Version: "0.4.1"}, - {Name: "github.com/prometheus/procfs", Version: "0.0.2"}, - {Name: "github.com/rcrowley/go-metrics", Version: "0.0.0-20181016184325-3113b8401b8a"}, - {Name: "github.com/remyoudompheng/bigfft", Version: "0.0.0-20170806203942-52369c62f446"}, - {Name: "github.com/rogpeppe/fastuuid", Version: "0.0.0-20150106093220-6724a57986af"}, - {Name: "github.com/rogpeppe/go-charset", Version: "0.0.0-20180617210344-2471d30d28b4"}, - {Name: "github.com/rogpeppe/go-internal", Version: "1.3.0"}, - {Name: "github.com/rubiojr/go-vhd", Version: "0.0.0-20160810183302-0bfd3b39853c"}, - {Name: "github.com/russross/blackfriday", Version: "1.5.2"}, - {Name: "github.com/russross/blackfriday/v2", Version: "2.0.1"}, - {Name: "github.com/saracen/walker", Version: "0.0.0-20191201085201-324a081bae7e"}, - {Name: "github.com/satori/go.uuid", Version: "1.2.0"}, - {Name: "github.com/sclevine/spec", Version: "1.2.0"}, - {Name: "github.com/sergi/go-diff", Version: "1.1.0"}, - {Name: "github.com/shurcooL/sanitized_anchor_name", Version: "1.0.0"}, - {Name: "github.com/simplereach/timeutils", Version: "1.2.0"}, - {Name: "github.com/sirupsen/logrus", Version: "1.5.0"}, - {Name: "github.com/smartystreets/assertions", Version: "1.2.0"}, - {Name: "github.com/smartystreets/goconvey", Version: "1.6.4"}, - {Name: "github.com/soheilhy/cmux", Version: "0.1.4"}, - {Name: "github.com/sosedoff/gitkit", Version: "0.2.0"}, - {Name: "github.com/spf13/afero", Version: "1.2.2"}, - {Name: "github.com/spf13/cast", Version: "1.3.0"}, - {Name: "github.com/spf13/cobra", Version: "0.0.5"}, - {Name: "github.com/spf13/jwalterweatherman", Version: "1.0.0"}, - {Name: "github.com/spf13/pflag", Version: "1.0.5"}, - {Name: "github.com/spf13/viper", Version: "1.3.2"}, - {Name: "github.com/stretchr/objx", Version: "0.3.0"}, - {Name: "github.com/stretchr/testify", Version: "1.6.1"}, - {Name: "github.com/testcontainers/testcontainers-go", Version: "0.3.1"}, - {Name: "github.com/tmc/grpc-websocket-proxy", Version: "0.0.0-20170815181823-89b8d40f7ca8"}, - {Name: "github.com/twitchtv/twirp", Version: "5.10.1+incompatible"}, - {Name: "github.com/ugorji/go", Version: "1.1.7"}, - {Name: "github.com/ugorji/go/codec", Version: "1.1.7"}, - {Name: "github.com/urfave/cli", Version: "1.22.5"}, - {Name: "github.com/urfave/cli/v2", Version: "2.3.0"}, - {Name: "github.com/vdemeester/k8s-pkg-credentialprovider", Version: "1.17.4"}, - {Name: "github.com/vmware/govmomi", Version: "0.20.3"}, - {Name: "github.com/xanzy/ssh-agent", Version: "0.2.1"}, - {Name: "github.com/xiang90/probing", Version: "0.0.0-20190116061207-43a291ad63a2"}, - {Name: "github.com/xordataexchange/crypt", Version: "0.0.3-0.20170626215501-b2862e3d0a77"}, - {Name: "github.com/yashtewari/glob-intersection", Version: "0.0.0-20180916065949-5c77d914dd0b"}, - {Name: "github.com/yuin/goldmark", Version: "1.1.32"}, - {Name: "github.com/yuin/gopher-lua", Version: "0.0.0-20191220021717-ab39c6098bdb"}, - {Name: "go.etcd.io/bbolt", Version: "1.3.5"}, - {Name: "go.etcd.io/etcd", Version: "0.0.0-20191023171146-3cf2f69b5738"}, - {Name: "go.opencensus.io", Version: "0.22.4"}, - {Name: "go.opentelemetry.io/otel", Version: "0.14.0"}, - {Name: "go.uber.org/atomic", Version: "1.5.1"}, - {Name: "go.uber.org/multierr", Version: "1.4.0"}, - {Name: "go.uber.org/tools", Version: "0.0.0-20190618225709-2cfd321de3ee"}, - {Name: "go.uber.org/zap", Version: "1.13.0"}, - {Name: "golang.org/x/crypto", Version: "0.0.0-20201002170205-7f63de1d35b0"}, - {Name: "golang.org/x/exp", Version: "0.0.0-20200224162631-6cc2880d07d6"}, - {Name: "golang.org/x/image", Version: "0.0.0-20190802002840-cff245a6509b"}, - {Name: "golang.org/x/lint", Version: "0.0.0-20200302205851-738671d3881b"}, - {Name: "golang.org/x/mobile", Version: "0.0.0-20190719004257-d2bd2a29d028"}, - {Name: "golang.org/x/mod", Version: "0.3.0"}, - {Name: "golang.org/x/net", Version: "0.0.0-20201006153459-a7d1128ccaa0"}, - {Name: "golang.org/x/oauth2", Version: "0.0.0-20201208152858-08078c50e5b5"}, - {Name: "golang.org/x/sync", Version: "0.0.0-20200625203802-6e8e738ad208"}, - {Name: "golang.org/x/sys", Version: "0.0.0-20201006155630-ac719f4daadf"}, - {Name: "golang.org/x/text", Version: "0.3.3"}, - {Name: "golang.org/x/time", Version: "0.0.0-20191024005414-555d28b269f0"}, - {Name: "golang.org/x/tools", Version: "0.0.0-20200825202427-b303f430e36d"}, - {Name: "golang.org/x/xerrors", Version: "0.0.0-20200804184101-5ec99f83aff1"}, - {Name: "gonum.org/v1/gonum", Version: "0.0.0-20190331200053-3d26580ed485"}, - {Name: "gonum.org/v1/netlib", Version: "0.0.0-20190331212654-76723241ea4e"}, - {Name: "google.golang.org/api", Version: "0.30.0"}, - {Name: "google.golang.org/appengine", Version: "1.6.6"}, - {Name: "google.golang.org/genproto", Version: "0.0.0-20200825200019-8632dd797987"}, - {Name: "google.golang.org/grpc", Version: "1.31.0"}, - {Name: "google.golang.org/protobuf", Version: "1.25.0"}, - {Name: "gopkg.in/alecthomas/kingpin.v2", Version: "2.2.6"}, - {Name: "gopkg.in/check.v1", Version: "1.0.0-20200902074654-038fdea0a05b"}, - {Name: "gopkg.in/cheggaaa/pb.v1", Version: "1.0.28"}, - {Name: "gopkg.in/errgo.v2", Version: "2.1.0"}, - {Name: "gopkg.in/fsnotify.v1", Version: "1.4.7"}, - {Name: "gopkg.in/gcfg.v1", Version: "1.2.0"}, - {Name: "gopkg.in/go-playground/assert.v1", Version: "1.2.1"}, - {Name: "gopkg.in/go-playground/validator.v9", Version: "9.31.0"}, - {Name: "gopkg.in/inf.v0", Version: "0.9.1"}, - {Name: "gopkg.in/mgo.v2", Version: "2.0.0-20180705113604-9856a29383ce"}, - {Name: "gopkg.in/natefinch/lumberjack.v2", Version: "2.0.0"}, - {Name: "gopkg.in/resty.v1", Version: "1.12.0"}, - {Name: "gopkg.in/square/go-jose.v2", Version: "2.2.2"}, - {Name: "gopkg.in/tomb.v1", Version: "1.0.0-20141024135613-dd632973f1e7"}, - {Name: "gopkg.in/warnings.v0", Version: "0.1.2"}, - {Name: "gopkg.in/yaml.v2", Version: "2.4.0"}, - {Name: "gopkg.in/yaml.v3", Version: "3.0.0-20200615113413-eeeca48fe776"}, - {Name: "gotest.tools", Version: "2.2.0+incompatible"}, - {Name: "honnef.co/go/tools", Version: "0.0.1-2020.1.4"}, - {Name: "k8s.io/api", Version: "0.17.4"}, - {Name: "k8s.io/apimachinery", Version: "0.17.4"}, - {Name: "k8s.io/apiserver", Version: "0.17.4"}, - {Name: "k8s.io/client-go", Version: "0.17.4"}, - {Name: "k8s.io/cloud-provider", Version: "0.17.4"}, - {Name: "k8s.io/code-generator", Version: "0.17.2"}, - {Name: "k8s.io/component-base", Version: "0.17.4"}, - {Name: "k8s.io/csi-translation-lib", Version: "0.17.4"}, - {Name: "k8s.io/gengo", Version: "0.0.0-20190822140433-26a664648505"}, - {Name: "k8s.io/klog", Version: "1.0.0"}, - {Name: "k8s.io/klog/v2", Version: "2.0.0"}, - {Name: "k8s.io/kube-openapi", Version: "0.0.0-20191107075043-30be4d16710a"}, - {Name: "k8s.io/legacy-cloud-providers", Version: "0.17.4"}, - {Name: "k8s.io/utils", Version: "0.0.0-20201110183641-67b214c5f920"}, - {Name: "modernc.org/cc", Version: "1.0.0"}, - {Name: "modernc.org/golex", Version: "1.0.0"}, - {Name: "modernc.org/mathutil", Version: "1.0.0"}, - {Name: "modernc.org/strutil", Version: "1.0.0"}, - {Name: "modernc.org/xc", Version: "1.0.0"}, - {Name: "moul.io/http2curl", Version: "1.0.0"}, - {Name: "rsc.io/binaryregexp", Version: "0.2.0"}, - {Name: "rsc.io/quote/v3", Version: "3.1.0"}, - {Name: "rsc.io/sampler", Version: "1.3.0"}, - {Name: "sigs.k8s.io/structured-merge-diff", Version: "1.0.1-0.20191108220359-b1b620dd3f06"}, - {Name: "sigs.k8s.io/yaml", Version: "1.1.0"}, + {Name: "cloud.google.com/go", Version: "v0.65.0"}, + {Name: "cloud.google.com/go/bigquery", Version: "v1.8.0"}, + {Name: "cloud.google.com/go/datastore", Version: "v1.1.0"}, + {Name: "cloud.google.com/go/pubsub", Version: "v1.3.1"}, + {Name: "cloud.google.com/go/storage", Version: "v1.10.0"}, + {Name: "dmitri.shuralyov.com/gpu/mtl", Version: "v0.0.0-20190408044501-666a987793e9"}, + {Name: "github.com/Azure/azure-sdk-for-go", Version: "v38.0.0+incompatible"}, + {Name: "github.com/Azure/go-ansiterm", Version: "v0.0.0-20170929234023-d6e3b3328b78"}, + {Name: "github.com/Azure/go-autorest/autorest", Version: "v0.9.3"}, + {Name: "github.com/Azure/go-autorest/autorest/adal", Version: "v0.8.1"}, + {Name: "github.com/Azure/go-autorest/autorest/date", Version: "v0.2.0"}, + {Name: "github.com/Azure/go-autorest/autorest/mocks", Version: "v0.3.0"}, + {Name: "github.com/Azure/go-autorest/autorest/to", Version: "v0.3.0"}, + {Name: "github.com/Azure/go-autorest/autorest/validation", Version: "v0.1.0"}, + {Name: "github.com/Azure/go-autorest/logger", Version: "v0.1.0"}, + {Name: "github.com/Azure/go-autorest/tracing", Version: "v0.5.0"}, + {Name: "github.com/BurntSushi/toml", Version: "v0.3.1"}, + {Name: "github.com/BurntSushi/xgb", Version: "v0.0.0-20160522181843-27f122750802"}, + {Name: "github.com/GoogleCloudPlatform/docker-credential-gcr", Version: "v1.5.0"}, + {Name: "github.com/GoogleCloudPlatform/k8s-cloud-provider", Version: "v0.0.0-20190822182118-27a4ced34534"}, + {Name: "github.com/Microsoft/go-winio", Version: "v0.4.15-0.20190919025122-fc70bd9a86b5"}, + {Name: "github.com/Microsoft/hcsshim", Version: "v0.8.6"}, + {Name: "github.com/NYTimes/gziphandler", Version: "v0.0.0-20170623195520-56545f4a5d46"}, + {Name: "github.com/OneOfOne/xxhash", Version: "v1.2.7"}, + {Name: "github.com/PuerkitoBio/purell", Version: "v1.1.1"}, + {Name: "github.com/PuerkitoBio/urlesc", Version: "v0.0.0-20170810143723-de5bf2ad4578"}, + {Name: "github.com/VividCortex/ewma", Version: "v1.1.1"}, + {Name: "github.com/alcortesm/tgz", Version: "v0.0.0-20161220082320-9c5fe88206d7"}, + {Name: "github.com/alecthomas/template", Version: "v0.0.0-20160405071501-a0175ee3bccc"}, + {Name: "github.com/alecthomas/units", Version: "v0.0.0-20151022065526-2efee857e7cf"}, + {Name: "github.com/alicebob/gopher-json", Version: "v0.0.0-20200520072559-a9ecdc9d1d3a"}, + {Name: "github.com/alicebob/miniredis/v2", Version: "v2.14.1"}, + {Name: "github.com/anmitsu/go-shlex", Version: "v0.0.0-20161002113705-648efa622239"}, + {Name: "github.com/aquasecurity/bolt-fixtures", Version: "v0.0.0-20200903104109-d34e7f983986"}, + {Name: "github.com/aquasecurity/fanal", Version: "v0.0.0-20210119051230-28c249da7cfd"}, + {Name: "github.com/aquasecurity/go-dep-parser", Version: "v0.0.0-20201028043324-889d4a92b8e0"}, + {Name: "github.com/aquasecurity/go-gem-version", Version: "v0.0.0-20201115065557-8eed6fe000ce"}, + {Name: "github.com/aquasecurity/go-npm-version", Version: "v0.0.0-20201110091526-0b796d180798"}, + {Name: "github.com/aquasecurity/go-pep440-version", Version: "v0.0.0-20210121094942-22b2f8951d46"}, + {Name: "github.com/aquasecurity/go-version", Version: "v0.0.0-20210121072130-637058cfe492"}, + {Name: "github.com/aquasecurity/testdocker", Version: "v0.0.0-20210106133225-0b17fe083674"}, + {Name: "github.com/aquasecurity/trivy", Version: "v0.16.0"}, + {Name: "github.com/aquasecurity/trivy-db", Version: "v0.0.0-20210105160501-c5bf4e153277"}, + {Name: "github.com/aquasecurity/vuln-list-update", Version: "v0.0.0-20191016075347-3d158c2bf9a2"}, + {Name: "github.com/araddon/dateparse", Version: "v0.0.0-20190426192744-0d74ffceef83"}, + {Name: "github.com/armon/consul-api", Version: "v0.0.0-20180202201655-eb2c6b5be1b6"}, + {Name: "github.com/armon/go-socks5", Version: "v0.0.0-20160902184237-e75332964ef5"}, + {Name: "github.com/aws/aws-sdk-go", Version: "v1.27.1"}, + {Name: "github.com/beorn7/perks", Version: "v1.0.0"}, + {Name: "github.com/bgentry/speakeasy", Version: "v0.1.0"}, + {Name: "github.com/blang/semver", Version: "v3.5.0+incompatible"}, + {Name: "github.com/briandowns/spinner", Version: "v1.12.0"}, + {Name: "github.com/caarlos0/env/v6", Version: "v6.0.0"}, + {Name: "github.com/cenkalti/backoff", Version: "v2.2.1+incompatible"}, + {Name: "github.com/census-instrumentation/opencensus-proto", Version: "v0.2.1"}, + {Name: "github.com/cespare/xxhash/v2", Version: "v2.1.1"}, + {Name: "github.com/cheggaaa/pb/v3", Version: "v3.0.3"}, + {Name: "github.com/chzyer/logex", Version: "v1.1.10"}, + {Name: "github.com/chzyer/readline", Version: "v0.0.0-20180603132655-2972be24d48e"}, + {Name: "github.com/chzyer/test", Version: "v0.0.0-20180213035817-a1ea475d72b1"}, + {Name: "github.com/client9/misspell", Version: "v0.3.4"}, + {Name: "github.com/cncf/udpa/go", Version: "v0.0.0-20191209042840-269d4d468f6f"}, + {Name: "github.com/cockroachdb/datadriven", Version: "v0.0.0-20190809214429-80d97fb3cbaa"}, + {Name: "github.com/containerd/containerd", Version: "v1.3.3"}, + {Name: "github.com/containerd/continuity", Version: "v0.0.0-20190426062206-aaeac12a7ffc"}, + {Name: "github.com/coreos/etcd", Version: "v3.3.10+incompatible"}, + {Name: "github.com/coreos/go-etcd", Version: "v2.0.0+incompatible"}, + {Name: "github.com/coreos/go-oidc", Version: "v2.1.0+incompatible"}, + {Name: "github.com/coreos/go-semver", Version: "v0.3.0"}, + {Name: "github.com/coreos/go-systemd", Version: "v0.0.0-20190321100706-95778dfbb74e"}, + {Name: "github.com/coreos/pkg", Version: "v0.0.0-20180108230652-97fdf19511ea"}, + {Name: "github.com/cpuguy83/go-md2man", Version: "v1.0.10"}, + {Name: "github.com/cpuguy83/go-md2man/v2", Version: "v2.0.0"}, + {Name: "github.com/creack/pty", Version: "v1.1.9"}, + {Name: "github.com/davecgh/go-spew", Version: "v1.1.1"}, + {Name: "github.com/deckarep/golang-set", Version: "v1.7.1"}, + {Name: "github.com/dgrijalva/jwt-go", Version: "v3.2.0+incompatible"}, + {Name: "github.com/dgryski/go-rendezvous", Version: "v0.0.0-20200823014737-9f7001d12a5f"}, + {Name: "github.com/dnaeon/go-vcr", Version: "v1.0.1"}, + {Name: "github.com/docker/cli", Version: "v0.0.0-20191017083524-a8ff7f821017"}, + {Name: "github.com/docker/distribution", Version: "v2.7.1+incompatible"}, + {Name: "github.com/docker/docker", Version: "v1.4.2-0.20190924003213-a8608b5b67c7"}, + {Name: "github.com/docker/docker-credential-helpers", Version: "v0.6.3"}, + {Name: "github.com/docker/go-connections", Version: "v0.4.0"}, + {Name: "github.com/docker/go-units", Version: "v0.4.0"}, + {Name: "github.com/docker/spdystream", Version: "v0.0.0-20160310174837-449fdfce4d96"}, + {Name: "github.com/dustin/go-humanize", Version: "v1.0.0"}, + {Name: "github.com/elazarl/goproxy", Version: "v0.0.0-20200809112317-0581fc3aee2d"}, + {Name: "github.com/elazarl/goproxy/ext", Version: "v0.0.0-20200809112317-0581fc3aee2d"}, + {Name: "github.com/emicklei/go-restful", Version: "v2.9.5+incompatible"}, + {Name: "github.com/emirpasic/gods", Version: "v1.12.0"}, + {Name: "github.com/envoyproxy/go-control-plane", Version: "v0.9.4"}, + {Name: "github.com/envoyproxy/protoc-gen-validate", Version: "v0.1.0"}, + {Name: "github.com/evanphx/json-patch", Version: "v4.2.0+incompatible"}, + {Name: "github.com/fatih/color", Version: "v1.10.0"}, + {Name: "github.com/flynn/go-shlex", Version: "v0.0.0-20150515145356-3f9db97f8568"}, + {Name: "github.com/fsnotify/fsnotify", Version: "v1.4.9"}, + {Name: "github.com/ghodss/yaml", Version: "v1.0.0"}, + {Name: "github.com/gin-contrib/sse", Version: "v0.1.0"}, + {Name: "github.com/gin-gonic/gin", Version: "v1.5.0"}, + {Name: "github.com/gliderlabs/ssh", Version: "v0.2.2"}, + {Name: "github.com/go-git/gcfg", Version: "v1.5.0"}, + {Name: "github.com/go-git/go-billy/v5", Version: "v5.0.0"}, + {Name: "github.com/go-git/go-git-fixtures/v4", Version: "v4.0.1"}, + {Name: "github.com/go-git/go-git/v5", Version: "v5.0.0"}, + {Name: "github.com/go-gl/glfw", Version: "v0.0.0-20190409004039-e6da0acd62b1"}, + {Name: "github.com/go-gl/glfw/v3.3/glfw", Version: "v0.0.0-20200222043503-6f7a984d4dc4"}, + {Name: "github.com/go-kit/kit", Version: "v0.8.0"}, + {Name: "github.com/go-logfmt/logfmt", Version: "v0.3.0"}, + {Name: "github.com/go-logr/logr", Version: "v0.1.0"}, + {Name: "github.com/go-openapi/jsonpointer", Version: "v0.19.3"}, + {Name: "github.com/go-openapi/jsonreference", Version: "v0.19.3"}, + {Name: "github.com/go-openapi/spec", Version: "v0.19.3"}, + {Name: "github.com/go-openapi/swag", Version: "v0.19.5"}, + {Name: "github.com/go-playground/locales", Version: "v0.13.0"}, + {Name: "github.com/go-playground/universal-translator", Version: "v0.17.0"}, + {Name: "github.com/go-redis/redis", Version: "v6.15.7+incompatible"}, + {Name: "github.com/go-redis/redis/v8", Version: "v8.4.0"}, + {Name: "github.com/go-restruct/restruct", Version: "v0.0.0-20191227155143-5734170a48a1"}, + {Name: "github.com/go-sql-driver/mysql", Version: "v1.5.0"}, + {Name: "github.com/go-stack/stack", Version: "v1.8.0"}, + {Name: "github.com/gobwas/glob", Version: "v0.2.3"}, + {Name: "github.com/goccy/go-yaml", Version: "v1.8.2"}, + {Name: "github.com/gogo/protobuf", Version: "v1.3.1"}, + {Name: "github.com/golang/glog", Version: "v0.0.0-20160126235308-23def4e6c14b"}, + {Name: "github.com/golang/groupcache", Version: "v0.0.0-20200121045136-8c9f03a8e57e"}, + {Name: "github.com/golang/mock", Version: "v1.4.4"}, + {Name: "github.com/golang/protobuf", Version: "v1.4.2"}, + {Name: "github.com/google/btree", Version: "v1.0.0"}, + {Name: "github.com/google/go-cmp", Version: "v0.5.3"}, + {Name: "github.com/google/go-containerregistry", Version: "v0.0.0-20200331213917-3d03ed9b1ca2"}, + {Name: "github.com/google/go-github/v28", Version: "v28.1.1"}, + {Name: "github.com/google/go-querystring", Version: "v1.0.0"}, + {Name: "github.com/google/gofuzz", Version: "v1.0.0"}, + {Name: "github.com/google/martian", Version: "v2.1.0+incompatible"}, + {Name: "github.com/google/martian/v3", Version: "v3.0.0"}, + {Name: "github.com/google/pprof", Version: "v0.0.0-20200708004538-1a94d8640e99"}, + {Name: "github.com/google/renameio", Version: "v0.1.0"}, + {Name: "github.com/google/subcommands", Version: "v1.0.1"}, + {Name: "github.com/google/uuid", Version: "v1.1.1"}, + {Name: "github.com/google/wire", Version: "v0.3.0"}, + {Name: "github.com/googleapis/gax-go/v2", Version: "v2.0.5"}, + {Name: "github.com/googleapis/gnostic", Version: "v0.2.2"}, + {Name: "github.com/gophercloud/gophercloud", Version: "v0.1.0"}, + {Name: "github.com/gopherjs/gopherjs", Version: "v0.0.0-20200217142428-fce0ec30dd00"}, + {Name: "github.com/gorilla/context", Version: "v1.1.1"}, + {Name: "github.com/gorilla/mux", Version: "v1.7.4"}, + {Name: "github.com/gorilla/websocket", Version: "v1.4.0"}, + {Name: "github.com/gregjones/httpcache", Version: "v0.0.0-20180305231024-9cad4c3443a7"}, + {Name: "github.com/grpc-ecosystem/go-grpc-middleware", Version: "v1.0.1-0.20190118093823-f849b5445de4"}, + {Name: "github.com/grpc-ecosystem/go-grpc-prometheus", Version: "v1.2.0"}, + {Name: "github.com/grpc-ecosystem/grpc-gateway", Version: "v1.9.5"}, + {Name: "github.com/hashicorp/errwrap", Version: "v1.0.0"}, + {Name: "github.com/hashicorp/go-multierror", Version: "v1.1.0"}, + {Name: "github.com/hashicorp/go-version", Version: "v1.2.1"}, + {Name: "github.com/hashicorp/golang-lru", Version: "v0.5.3"}, + {Name: "github.com/hashicorp/hcl", Version: "v1.0.0"}, + {Name: "github.com/hpcloud/tail", Version: "v1.0.0"}, + {Name: "github.com/ianlancetaylor/demangle", Version: "v0.0.0-20181102032728-5e5cf60278f6"}, + {Name: "github.com/imdario/mergo", Version: "v0.3.5"}, + {Name: "github.com/inconshreveable/mousetrap", Version: "v1.0.0"}, + {Name: "github.com/jbenet/go-context", Version: "v0.0.0-20150711004518-d14ea06fba99"}, + {Name: "github.com/jessevdk/go-flags", Version: "v1.4.0"}, + {Name: "github.com/jmespath/go-jmespath", Version: "v0.0.0-20180206201540-c2b33e8439af"}, + {Name: "github.com/joefitzgerald/rainbow-reporter", Version: "v0.1.0"}, + {Name: "github.com/jonboulle/clockwork", Version: "v0.1.0"}, + {Name: "github.com/json-iterator/go", Version: "v1.1.8"}, + {Name: "github.com/jstemmer/go-junit-report", Version: "v0.9.1"}, + {Name: "github.com/jtolds/gls", Version: "v4.20.0+incompatible"}, + {Name: "github.com/julienschmidt/httprouter", Version: "v1.2.0"}, + {Name: "github.com/kevinburke/ssh_config", Version: "v0.0.0-20190725054713-01f96b0aa0cd"}, + {Name: "github.com/kisielk/errcheck", Version: "v1.2.0"}, + {Name: "github.com/kisielk/gotool", Version: "v1.0.0"}, + {Name: "github.com/knqyf263/go-apk-version", Version: "v0.0.0-20200609155635-041fdbb8563f"}, + {Name: "github.com/knqyf263/go-deb-version", Version: "v0.0.0-20190517075300-09fca494f03d"}, + {Name: "github.com/knqyf263/go-rpm-version", Version: "v0.0.0-20170716094938-74609b86c936"}, + {Name: "github.com/knqyf263/go-rpmdb", Version: "v0.0.0-20201215100354-a9e3110d8ee1"}, + {Name: "github.com/knqyf263/nested", Version: "v0.0.1"}, + {Name: "github.com/konsorten/go-windows-terminal-sequences", Version: "v1.0.2"}, + {Name: "github.com/kr/logfmt", Version: "v0.0.0-20140226030751-b84e30acd515"}, + {Name: "github.com/kr/pretty", Version: "v0.1.0"}, + {Name: "github.com/kr/pty", Version: "v1.1.5"}, + {Name: "github.com/kr/text", Version: "v0.2.0"}, + {Name: "github.com/kylelemons/godebug", Version: "v1.1.0"}, + {Name: "github.com/leodido/go-urn", Version: "v1.2.0"}, + {Name: "github.com/magiconair/properties", Version: "v1.8.0"}, + {Name: "github.com/mailru/easyjson", Version: "v0.7.0"}, + {Name: "github.com/mattn/go-colorable", Version: "v0.1.8"}, + {Name: "github.com/mattn/go-isatty", Version: "v0.0.12"}, + {Name: "github.com/mattn/go-jsonpointer", Version: "v0.0.0-20180225143300-37667080efed"}, + {Name: "github.com/mattn/go-runewidth", Version: "v0.0.9"}, + {Name: "github.com/matttproud/golang_protobuf_extensions", Version: "v1.0.1"}, + {Name: "github.com/maxbrunsfeld/counterfeiter/v6", Version: "v6.2.2"}, + {Name: "github.com/mitchellh/go-homedir", Version: "v1.1.0"}, + {Name: "github.com/mitchellh/mapstructure", Version: "v1.1.2"}, + {Name: "github.com/modern-go/concurrent", Version: "v0.0.0-20180306012644-bacd9c7ef1dd"}, + {Name: "github.com/modern-go/reflect2", Version: "v1.0.1"}, + {Name: "github.com/morikuni/aec", Version: "v1.0.0"}, + {Name: "github.com/munnerz/goautoneg", Version: "v0.0.0-20191010083416-a7dc8b61c822"}, + {Name: "github.com/mwitkow/go-conntrack", Version: "v0.0.0-20161129095857-cc309e4a2223"}, + {Name: "github.com/mxk/go-flowrate", Version: "v0.0.0-20140419014527-cca7078d478f"}, + {Name: "github.com/niemeyer/pretty", Version: "v0.0.0-20200227124842-a10e7caefd8e"}, + {Name: "github.com/nxadm/tail", Version: "v1.4.4"}, + {Name: "github.com/olekukonko/tablewriter", Version: "v0.0.2-0.20190607075207-195002e6e56a"}, + {Name: "github.com/onsi/ginkgo", Version: "v1.14.2"}, + {Name: "github.com/onsi/gomega", Version: "v1.10.3"}, + {Name: "github.com/open-policy-agent/opa", Version: "v0.21.1"}, + {Name: "github.com/opencontainers/go-digest", Version: "v1.0.0-rc1"}, + {Name: "github.com/opencontainers/image-spec", Version: "v1.0.2-0.20190823105129-775207bd45b6"}, + {Name: "github.com/opencontainers/runc", Version: "v0.1.1"}, + {Name: "github.com/parnurzeal/gorequest", Version: "v0.2.16"}, + {Name: "github.com/pelletier/go-toml", Version: "v1.2.0"}, + {Name: "github.com/peterbourgon/diskv", Version: "v2.0.1+incompatible"}, + {Name: "github.com/peterh/liner", Version: "v0.0.0-20170211195444-bf27d3ba8e1d"}, + {Name: "github.com/pkg/errors", Version: "v0.9.1"}, + {Name: "github.com/pmezard/go-difflib", Version: "v1.0.0"}, + {Name: "github.com/pquerna/cachecontrol", Version: "v0.0.0-20171018203845-0dec1b30a021"}, + {Name: "github.com/prometheus/client_golang", Version: "v1.0.0"}, + {Name: "github.com/prometheus/client_model", Version: "v0.0.0-20190812154241-14fe0d1b01d4"}, + {Name: "github.com/prometheus/common", Version: "v0.4.1"}, + {Name: "github.com/prometheus/procfs", Version: "v0.0.2"}, + {Name: "github.com/rcrowley/go-metrics", Version: "v0.0.0-20181016184325-3113b8401b8a"}, + {Name: "github.com/remyoudompheng/bigfft", Version: "v0.0.0-20170806203942-52369c62f446"}, + {Name: "github.com/rogpeppe/fastuuid", Version: "v0.0.0-20150106093220-6724a57986af"}, + {Name: "github.com/rogpeppe/go-charset", Version: "v0.0.0-20180617210344-2471d30d28b4"}, + {Name: "github.com/rogpeppe/go-internal", Version: "v1.3.0"}, + {Name: "github.com/rubiojr/go-vhd", Version: "v0.0.0-20160810183302-0bfd3b39853c"}, + {Name: "github.com/russross/blackfriday", Version: "v1.5.2"}, + {Name: "github.com/russross/blackfriday/v2", Version: "v2.0.1"}, + {Name: "github.com/saracen/walker", Version: "v0.0.0-20191201085201-324a081bae7e"}, + {Name: "github.com/satori/go.uuid", Version: "v1.2.0"}, + {Name: "github.com/sclevine/spec", Version: "v1.2.0"}, + {Name: "github.com/sergi/go-diff", Version: "v1.1.0"}, + {Name: "github.com/shurcooL/sanitized_anchor_name", Version: "v1.0.0"}, + {Name: "github.com/simplereach/timeutils", Version: "v1.2.0"}, + {Name: "github.com/sirupsen/logrus", Version: "v1.5.0"}, + {Name: "github.com/smartystreets/assertions", Version: "v1.2.0"}, + {Name: "github.com/smartystreets/goconvey", Version: "v1.6.4"}, + {Name: "github.com/soheilhy/cmux", Version: "v0.1.4"}, + {Name: "github.com/sosedoff/gitkit", Version: "v0.2.0"}, + {Name: "github.com/spf13/afero", Version: "v1.2.2"}, + {Name: "github.com/spf13/cast", Version: "v1.3.0"}, + {Name: "github.com/spf13/cobra", Version: "v0.0.5"}, + {Name: "github.com/spf13/jwalterweatherman", Version: "v1.0.0"}, + {Name: "github.com/spf13/pflag", Version: "v1.0.5"}, + {Name: "github.com/spf13/viper", Version: "v1.3.2"}, + {Name: "github.com/stretchr/objx", Version: "v0.3.0"}, + {Name: "github.com/stretchr/testify", Version: "v1.6.1"}, + {Name: "github.com/testcontainers/testcontainers-go", Version: "v0.3.1"}, + {Name: "github.com/tmc/grpc-websocket-proxy", Version: "v0.0.0-20170815181823-89b8d40f7ca8"}, + {Name: "github.com/twitchtv/twirp", Version: "v5.10.1+incompatible"}, + {Name: "github.com/ugorji/go", Version: "v1.1.7"}, + {Name: "github.com/ugorji/go/codec", Version: "v1.1.7"}, + {Name: "github.com/urfave/cli", Version: "v1.22.5"}, + {Name: "github.com/urfave/cli/v2", Version: "v2.3.0"}, + {Name: "github.com/vdemeester/k8s-pkg-credentialprovider", Version: "v1.17.4"}, + {Name: "github.com/vmware/govmomi", Version: "v0.20.3"}, + {Name: "github.com/xanzy/ssh-agent", Version: "v0.2.1"}, + {Name: "github.com/xiang90/probing", Version: "v0.0.0-20190116061207-43a291ad63a2"}, + {Name: "github.com/xordataexchange/crypt", Version: "v0.0.3-0.20170626215501-b2862e3d0a77"}, + {Name: "github.com/yashtewari/glob-intersection", Version: "v0.0.0-20180916065949-5c77d914dd0b"}, + {Name: "github.com/yuin/goldmark", Version: "v1.1.32"}, + {Name: "github.com/yuin/gopher-lua", Version: "v0.0.0-20191220021717-ab39c6098bdb"}, + {Name: "go.etcd.io/bbolt", Version: "v1.3.5"}, + {Name: "go.etcd.io/etcd", Version: "v0.0.0-20191023171146-3cf2f69b5738"}, + {Name: "go.opencensus.io", Version: "v0.22.4"}, + {Name: "go.opentelemetry.io/otel", Version: "v0.14.0"}, + {Name: "go.uber.org/atomic", Version: "v1.5.1"}, + {Name: "go.uber.org/multierr", Version: "v1.4.0"}, + {Name: "go.uber.org/tools", Version: "v0.0.0-20190618225709-2cfd321de3ee"}, + {Name: "go.uber.org/zap", Version: "v1.13.0"}, + {Name: "golang.org/x/crypto", Version: "v0.0.0-20201002170205-7f63de1d35b0"}, + {Name: "golang.org/x/exp", Version: "v0.0.0-20200224162631-6cc2880d07d6"}, + {Name: "golang.org/x/image", Version: "v0.0.0-20190802002840-cff245a6509b"}, + {Name: "golang.org/x/lint", Version: "v0.0.0-20200302205851-738671d3881b"}, + {Name: "golang.org/x/mobile", Version: "v0.0.0-20190719004257-d2bd2a29d028"}, + {Name: "golang.org/x/mod", Version: "v0.3.0"}, + {Name: "golang.org/x/net", Version: "v0.0.0-20201006153459-a7d1128ccaa0"}, + {Name: "golang.org/x/oauth2", Version: "v0.0.0-20201208152858-08078c50e5b5"}, + {Name: "golang.org/x/sync", Version: "v0.0.0-20200625203802-6e8e738ad208"}, + {Name: "golang.org/x/sys", Version: "v0.0.0-20201006155630-ac719f4daadf"}, + {Name: "golang.org/x/text", Version: "v0.3.3"}, + {Name: "golang.org/x/time", Version: "v0.0.0-20191024005414-555d28b269f0"}, + {Name: "golang.org/x/tools", Version: "v0.0.0-20200825202427-b303f430e36d"}, + {Name: "golang.org/x/xerrors", Version: "v0.0.0-20200804184101-5ec99f83aff1"}, + {Name: "gonum.org/v1/gonum", Version: "v0.0.0-20190331200053-3d26580ed485"}, + {Name: "gonum.org/v1/netlib", Version: "v0.0.0-20190331212654-76723241ea4e"}, + {Name: "google.golang.org/api", Version: "v0.30.0"}, + {Name: "google.golang.org/appengine", Version: "v1.6.6"}, + {Name: "google.golang.org/genproto", Version: "v0.0.0-20200825200019-8632dd797987"}, + {Name: "google.golang.org/grpc", Version: "v1.31.0"}, + {Name: "google.golang.org/protobuf", Version: "v1.25.0"}, + {Name: "gopkg.in/alecthomas/kingpin.v2", Version: "v2.2.6"}, + {Name: "gopkg.in/check.v1", Version: "v1.0.0-20200902074654-038fdea0a05b"}, + {Name: "gopkg.in/cheggaaa/pb.v1", Version: "v1.0.28"}, + {Name: "gopkg.in/errgo.v2", Version: "v2.1.0"}, + {Name: "gopkg.in/fsnotify.v1", Version: "v1.4.7"}, + {Name: "gopkg.in/gcfg.v1", Version: "v1.2.0"}, + {Name: "gopkg.in/go-playground/assert.v1", Version: "v1.2.1"}, + {Name: "gopkg.in/go-playground/validator.v9", Version: "v9.31.0"}, + {Name: "gopkg.in/inf.v0", Version: "v0.9.1"}, + {Name: "gopkg.in/mgo.v2", Version: "v2.0.0-20180705113604-9856a29383ce"}, + {Name: "gopkg.in/natefinch/lumberjack.v2", Version: "v2.0.0"}, + {Name: "gopkg.in/resty.v1", Version: "v1.12.0"}, + {Name: "gopkg.in/square/go-jose.v2", Version: "v2.2.2"}, + {Name: "gopkg.in/tomb.v1", Version: "v1.0.0-20141024135613-dd632973f1e7"}, + {Name: "gopkg.in/warnings.v0", Version: "v0.1.2"}, + {Name: "gopkg.in/yaml.v2", Version: "v2.4.0"}, + {Name: "gopkg.in/yaml.v3", Version: "v3.0.0-20200615113413-eeeca48fe776"}, + {Name: "gotest.tools", Version: "v2.2.0+incompatible"}, + {Name: "honnef.co/go/tools", Version: "v0.0.1-2020.1.4"}, + {Name: "k8s.io/api", Version: "v0.17.4"}, + {Name: "k8s.io/apimachinery", Version: "v0.17.4"}, + {Name: "k8s.io/apiserver", Version: "v0.17.4"}, + {Name: "k8s.io/client-go", Version: "v0.17.4"}, + {Name: "k8s.io/cloud-provider", Version: "v0.17.4"}, + {Name: "k8s.io/code-generator", Version: "v0.17.2"}, + {Name: "k8s.io/component-base", Version: "v0.17.4"}, + {Name: "k8s.io/csi-translation-lib", Version: "v0.17.4"}, + {Name: "k8s.io/gengo", Version: "v0.0.0-20190822140433-26a664648505"}, + {Name: "k8s.io/klog", Version: "v1.0.0"}, + {Name: "k8s.io/klog/v2", Version: "v2.0.0"}, + {Name: "k8s.io/kube-openapi", Version: "v0.0.0-20191107075043-30be4d16710a"}, + {Name: "k8s.io/legacy-cloud-providers", Version: "v0.17.4"}, + {Name: "k8s.io/utils", Version: "v0.0.0-20201110183641-67b214c5f920"}, + {Name: "modernc.org/cc", Version: "v1.0.0"}, + {Name: "modernc.org/golex", Version: "v1.0.0"}, + {Name: "modernc.org/mathutil", Version: "v1.0.0"}, + {Name: "modernc.org/strutil", Version: "v1.0.0"}, + {Name: "modernc.org/xc", Version: "v1.0.0"}, + {Name: "moul.io/http2curl", Version: "v1.0.0"}, + {Name: "rsc.io/binaryregexp", Version: "v0.2.0"}, + {Name: "rsc.io/quote/v3", Version: "v3.1.0"}, + {Name: "rsc.io/sampler", Version: "v1.3.0"}, + {Name: "sigs.k8s.io/structured-merge-diff", Version: "v1.0.1-0.20191108220359-b1b620dd3f06"}, + {Name: "sigs.k8s.io/yaml", Version: "v1.1.0"}, } ) diff --git a/pkg/fanal/analyzer/language/golang/binary/binary_test.go b/pkg/fanal/analyzer/language/golang/binary/binary_test.go index 70256113cfd3..041d43e1b45b 100644 --- a/pkg/fanal/analyzer/language/golang/binary/binary_test.go +++ b/pkg/fanal/analyzer/language/golang/binary/binary_test.go @@ -36,7 +36,7 @@ func Test_gobinaryLibraryAnalyzer_Analyze(t *testing.T) { }, { Name: "stdlib", - Version: "1.15.2", + Version: "v1.15.2", Relationship: types.RelationshipDirect, }, { diff --git a/pkg/fanal/analyzer/language/golang/mod/mod.go b/pkg/fanal/analyzer/language/golang/mod/mod.go index 398511fdc63c..52d7b32f3bee 100644 --- a/pkg/fanal/analyzer/language/golang/mod/mod.go +++ b/pkg/fanal/analyzer/language/golang/mod/mod.go @@ -148,7 +148,7 @@ func (a *gomodAnalyzer) fillAdditionalData(apps []types.Application) error { } // e.g. $GOPATH/pkg/mod/github.com/aquasecurity/go-dep-parser@v1.0.0 - modDir := filepath.Join(modPath, fmt.Sprintf("%s@v%s", normalizeModName(lib.Name), lib.Version)) + modDir := filepath.Join(modPath, fmt.Sprintf("%s@%s", normalizeModName(lib.Name), lib.Version)) // Collect licenses if licenseNames, err := findLicense(modDir, a.licenseClassifierConfidenceLevel); err != nil { diff --git a/pkg/fanal/analyzer/language/golang/mod/mod_test.go b/pkg/fanal/analyzer/language/golang/mod/mod_test.go index 5cf006430aca..3963bcebbad9 100644 --- a/pkg/fanal/analyzer/language/golang/mod/mod_test.go +++ b/pkg/fanal/analyzer/language/golang/mod/mod_test.go @@ -46,7 +46,7 @@ func Test_gomodAnalyzer_Analyze(t *testing.T) { { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20220406074731-71021a481237", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20220406074731-71021a481237", + Version: "v0.0.0-20220406074731-71021a481237", Relationship: types.RelationshipDirect, Licenses: []string{"MIT"}, ExternalReferences: []types.ExternalRef{ @@ -62,7 +62,7 @@ func Test_gomodAnalyzer_Analyze(t *testing.T) { { ID: "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1", Name: "golang.org/x/xerrors", - Version: "0.0.0-20200804184101-5ec99f83aff1", + Version: "v0.0.0-20200804184101-5ec99f83aff1", Relationship: types.RelationshipIndirect, Indirect: true, }, @@ -96,7 +96,7 @@ func Test_gomodAnalyzer_Analyze(t *testing.T) { { ID: "github.com/sad/sad@v0.0.1", Name: "github.com/sad/sad", - Version: "0.0.1", + Version: "v0.0.1", Relationship: types.RelationshipDirect, ExternalReferences: []types.ExternalRef{ { @@ -136,7 +136,7 @@ func Test_gomodAnalyzer_Analyze(t *testing.T) { { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20230219131432-590b1dfb6edd", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20230219131432-590b1dfb6edd", + Version: "v0.0.0-20230219131432-590b1dfb6edd", Relationship: types.RelationshipDirect, DependsOn: []string{ "github.com/BurntSushi/toml@v0.3.1", @@ -151,7 +151,7 @@ func Test_gomodAnalyzer_Analyze(t *testing.T) { { ID: "github.com/BurntSushi/toml@v0.3.1", Name: "github.com/BurntSushi/toml", - Version: "0.3.1", + Version: "v0.3.1", Relationship: types.RelationshipIndirect, Indirect: true, Licenses: []string{ @@ -188,7 +188,7 @@ func Test_gomodAnalyzer_Analyze(t *testing.T) { { ID: "github.com/aquasecurity/go-dep-parser@v0.0.0-20230219131432-590b1dfb6edd", Name: "github.com/aquasecurity/go-dep-parser", - Version: "0.0.0-20230219131432-590b1dfb6edd", + Version: "v0.0.0-20230219131432-590b1dfb6edd", Relationship: types.RelationshipDirect, DependsOn: []string{}, ExternalReferences: []types.ExternalRef{ diff --git a/pkg/purl/purl_test.go b/pkg/purl/purl_test.go index 25e9e7829d7b..d5ba15300393 100644 --- a/pkg/purl/purl_test.go +++ b/pkg/purl/purl_test.go @@ -809,38 +809,38 @@ func TestPackageURL_Match(t *testing.T) { }{ { name: "same purl", - constraint: "pkg:golang/github.com/aquasecurity/trivy@0.49.0", - target: "pkg:golang/github.com/aquasecurity/trivy@0.49.0", + constraint: "pkg:golang/github.com/aquasecurity/trivy@v0.49.0", + target: "pkg:golang/github.com/aquasecurity/trivy@v0.49.0", want: true, }, { name: "different type", - constraint: "pkg:golang/github.com/aquasecurity/trivy@0.49.0", + constraint: "pkg:golang/github.com/aquasecurity/trivy@v0.49.0", target: "pkg:maven/github.com/aquasecurity/trivy@0.49.0", want: false, }, { name: "different namespace", - constraint: "pkg:golang/github.com/aquasecurity/trivy@0.49.0", - target: "pkg:golang/github.com/aquasecurity2/trivy@0.49.0", + constraint: "pkg:golang/github.com/aquasecurity/trivy@v0.49.0", + target: "pkg:golang/github.com/aquasecurity2/trivy@v.49.0", want: false, }, { name: "different name", - constraint: "pkg:golang/github.com/aquasecurity/trivy@0.49.0", - target: "pkg:golang/github.com/aquasecurity/tracee@0.49.0", + constraint: "pkg:golang/github.com/aquasecurity/trivy@v0.49.0", + target: "pkg:golang/github.com/aquasecurity/tracee@v0.49.0", want: false, }, { name: "different version", - constraint: "pkg:golang/github.com/aquasecurity/trivy@0.49.0", - target: "pkg:golang/github.com/aquasecurity/trivy@0.49.1", + constraint: "pkg:golang/github.com/aquasecurity/trivy@v0.49.0", + target: "pkg:golang/github.com/aquasecurity/trivy@v0.49.1", want: false, }, { name: "version wildcard", constraint: "pkg:golang/github.com/aquasecurity/trivy", - target: "pkg:golang/github.com/aquasecurity/trivy@0.50.0", + target: "pkg:golang/github.com/aquasecurity/trivy@v0.50.0", want: true, }, { diff --git a/pkg/result/filter_test.go b/pkg/result/filter_test.go index 289ec2ee0c63..08b523dd2dde 100644 --- a/pkg/result/filter_test.go +++ b/pkg/result/filter_test.go @@ -21,16 +21,16 @@ import ( func TestFilter(t *testing.T) { var ( pkg1 = ftypes.Package{ - ID: "foo@1.2.3", + ID: "foo@v1.2.3", Name: "foo", - Version: "1.2.3", + Version: "v1.2.3", Identifier: ftypes.PkgIdentifier{ UID: "01", PURL: &packageurl.PackageURL{ Type: packageurl.TypeGolang, Namespace: "github.com/aquasecurity", Name: "foo", - Version: "1.2.3", + Version: "v1.2.3", }, }, } @@ -90,14 +90,14 @@ func TestFilter(t *testing.T) { vuln6 = types.DetectedVulnerability{ VulnerabilityID: "CVE-2019-0006", PkgName: "foo", - InstalledVersion: "1.2.3", + InstalledVersion: "v1.2.3", FixedVersion: "1.2.4", PkgIdentifier: ftypes.PkgIdentifier{ PURL: &packageurl.PackageURL{ Type: packageurl.TypeGolang, Namespace: "github.com/aquasecurity", Name: "foo", - Version: "1.2.3", + Version: "v1.2.3", }, }, Vulnerability: dbTypes.Vulnerability{ @@ -107,14 +107,14 @@ func TestFilter(t *testing.T) { vuln7 = types.DetectedVulnerability{ VulnerabilityID: "CVE-2019-0007", PkgName: "bar", - InstalledVersion: "2.3.4", + InstalledVersion: "v2.3.4", FixedVersion: "2.3.5", PkgIdentifier: ftypes.PkgIdentifier{ PURL: &packageurl.PackageURL{ Type: packageurl.TypeGolang, Namespace: "github.com/aquasecurity", Name: "bar", - Version: "2.3.4", + Version: "v2.3.4", }, }, Vulnerability: dbTypes.Vulnerability{ diff --git a/pkg/result/testdata/openvex.json b/pkg/result/testdata/openvex.json index dcdd344700c7..385f22e47deb 100644 --- a/pkg/result/testdata/openvex.json +++ b/pkg/result/testdata/openvex.json @@ -8,7 +8,7 @@ { "vulnerability": {"name": "CVE-2019-0001"}, "products": [ - {"@id": "pkg:golang/github.com/aquasecurity/foo@1.2.3"} + {"@id": "pkg:golang/github.com/aquasecurity/foo@v1.2.3"} ], "status": "not_affected", "justification": "vulnerable_code_not_in_execute_path" diff --git a/pkg/sbom/io/encode_test.go b/pkg/sbom/io/encode_test.go index 52fbed415933..5a5b821590b8 100644 --- a/pkg/sbom/io/encode_test.go +++ b/pkg/sbom/io/encode_test.go @@ -461,7 +461,7 @@ func TestEncoder_Encode(t *testing.T) { Type: packageurl.TypeGolang, Namespace: "github.com/org", Name: "direct", - Version: "1.0.0", + Version: "v1.0.0", }, }, Relationship: ftypes.RelationshipDirect, @@ -472,28 +472,28 @@ func TestEncoder_Encode(t *testing.T) { { ID: "github.com/org/indirect@v2.0.0", Name: "github.com/org/indirect", - Version: "2.0.0", + Version: "v2.0.0", Identifier: ftypes.PkgIdentifier{ UID: "955AB4E7E24AC085", PURL: &packageurl.PackageURL{ Type: packageurl.TypeGolang, Namespace: "github.com/org", Name: "indirect", - Version: "2.0.0", + Version: "v2.0.0", }, }, Relationship: ftypes.RelationshipIndirect, }, { - ID: "stdlib@1.22.1", + ID: "stdlib@v1.22.1", Name: "stdlib", - Version: "1.22.1", + Version: "v1.22.1", Identifier: ftypes.PkgIdentifier{ UID: "49728B9674E318A6", PURL: &packageurl.PackageURL{ Type: packageurl.TypeGolang, Name: "stdlib", - Version: "1.22.1", + Version: "v1.22.1", }, }, Relationship: ftypes.RelationshipDirect, @@ -561,7 +561,7 @@ func TestEncoder_Encode(t *testing.T) { uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000004"): { Type: core.TypeLibrary, Name: "github.com/org/direct", - Version: "1.0.0", + Version: "v1.0.0", SrcFile: "test", Properties: []core.Property{ { @@ -579,15 +579,15 @@ func TestEncoder_Encode(t *testing.T) { Type: packageurl.TypeGolang, Namespace: "github.com/org", Name: "direct", - Version: "1.0.0", + Version: "v1.0.0", }, - BOMRef: "pkg:golang/github.com/org/direct@1.0.0", + BOMRef: "pkg:golang/github.com/org/direct@v1.0.0", }, }, uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000005"): { Type: core.TypeLibrary, Name: "github.com/org/indirect", - Version: "2.0.0", + Version: "v2.0.0", SrcFile: "test", Properties: []core.Property{ { @@ -605,20 +605,20 @@ func TestEncoder_Encode(t *testing.T) { Type: packageurl.TypeGolang, Namespace: "github.com/org", Name: "indirect", - Version: "2.0.0", + Version: "v2.0.0", }, - BOMRef: "pkg:golang/github.com/org/indirect@2.0.0", + BOMRef: "pkg:golang/github.com/org/indirect@v2.0.0", }, }, uuid.MustParse("3ff14136-e09f-4df9-80ea-000000000006"): { Type: core.TypeLibrary, Name: "stdlib", - Version: "1.22.1", + Version: "v1.22.1", SrcFile: "test", Properties: []core.Property{ { Name: core.PropertyPkgID, - Value: "stdlib@1.22.1", + Value: "stdlib@v1.22.1", }, { Name: core.PropertyPkgType, @@ -630,9 +630,9 @@ func TestEncoder_Encode(t *testing.T) { PURL: &packageurl.PackageURL{ Type: packageurl.TypeGolang, Name: "stdlib", - Version: "1.22.1", + Version: "v1.22.1", }, - BOMRef: "pkg:golang/stdlib@1.22.1", + BOMRef: "pkg:golang/stdlib@v1.22.1", }, }, }, diff --git a/pkg/vex/testdata/csaf-relationships.json b/pkg/vex/testdata/csaf-relationships.json index 2e823d17a2c6..18c311228206 100644 --- a/pkg/vex/testdata/csaf-relationships.json +++ b/pkg/vex/testdata/csaf-relationships.json @@ -42,7 +42,7 @@ "name": "go-direct1 v2.0.0", "product_id": "go-direct1-v2.0.0", "product_identification_helper": { - "purl": "pkg:golang/github.com/aquasecurity/go-direct1@2.0.0" + "purl": "pkg:golang/github.com/aquasecurity/go-direct1@v2.0.0" } } } @@ -65,7 +65,7 @@ "name": "go-transitive v4.0.0", "product_id": "go-transitive-v4.0.0", "product_identification_helper": { - "purl": "pkg:golang/github.com/aquasecurity/go-transitive@4.0.0" + "purl": "pkg:golang/github.com/aquasecurity/go-transitive@v4.0.0" } } } diff --git a/pkg/vex/testdata/csaf.json b/pkg/vex/testdata/csaf.json index 70afefe70205..182856f96a51 100644 --- a/pkg/vex/testdata/csaf.json +++ b/pkg/vex/testdata/csaf.json @@ -50,7 +50,7 @@ "name": "go-transitive v4.0.0", "product_id": "go-transitive-v4.0.0", "product_identification_helper": { - "purl": "pkg:golang/github.com/aquasecurity/go-transitive@4.0.0" + "purl": "pkg:golang/github.com/aquasecurity/go-transitive@v4.0.0" } } } diff --git a/pkg/vex/testdata/openvex-nested.json b/pkg/vex/testdata/openvex-nested.json index da7dd68615a5..637603d9e48c 100644 --- a/pkg/vex/testdata/openvex-nested.json +++ b/pkg/vex/testdata/openvex-nested.json @@ -11,10 +11,10 @@ }, "products": [ { - "@id": "pkg:golang/github.com/aquasecurity/go-direct1@2.0.0", + "@id": "pkg:golang/github.com/aquasecurity/go-direct1@v2.0.0", "subcomponents": [ { - "@id": "pkg:golang/github.com/aquasecurity/go-transitive@4.0.0" + "@id": "pkg:golang/github.com/aquasecurity/go-transitive@v4.0.0" } ] } diff --git a/pkg/vex/vex_test.go b/pkg/vex/vex_test.go index 4a9686972a5e..bc7a5c069402 100644 --- a/pkg/vex/vex_test.go +++ b/pkg/vex/vex_test.go @@ -58,9 +58,9 @@ var ( }, } goModulePackage = ftypes.Package{ - ID: "github.com/aquasecurity/go-module@1.0.0", + ID: "github.com/aquasecurity/go-module@v1.0.0", Name: "github.com/aquasecurity/go-module", - Version: "1.0.0", + Version: "v1.0.0", Relationship: ftypes.RelationshipRoot, Identifier: ftypes.PkgIdentifier{ UID: "03", @@ -68,14 +68,14 @@ var ( Type: packageurl.TypeGolang, Namespace: "github.com/aquasecurity", Name: "go-module", - Version: "1.0.0", + Version: "v1.0.0", }, }, } goDirectPackage1 = ftypes.Package{ - ID: "github.com/aquasecurity/go-direct1@2.0.0", + ID: "github.com/aquasecurity/go-direct1@v2.0.0", Name: "github.com/aquasecurity/go-direct1", - Version: "2.0.0", + Version: "v2.0.0", Relationship: ftypes.RelationshipDirect, Identifier: ftypes.PkgIdentifier{ UID: "04", @@ -83,14 +83,14 @@ var ( Type: packageurl.TypeGolang, Namespace: "github.com/aquasecurity", Name: "go-direct1", - Version: "2.0.0", + Version: "v2.0.0", }, }, } goDirectPackage2 = ftypes.Package{ - ID: "github.com/aquasecurity/go-direct2@3.0.0", + ID: "github.com/aquasecurity/go-direct2@v3.0.0", Name: "github.com/aquasecurity/go-direct2", - Version: "3.0.0", + Version: "v3.0.0", Relationship: ftypes.RelationshipDirect, Identifier: ftypes.PkgIdentifier{ UID: "05", @@ -98,14 +98,14 @@ var ( Type: packageurl.TypeGolang, Namespace: "github.com/aquasecurity", Name: "go-direct2", - Version: "3.0.0", + Version: "v3.0.0", }, }, } goTransitivePackage = ftypes.Package{ - ID: "github.com/aquasecurity/go-transitive@4.0.0", + ID: "github.com/aquasecurity/go-transitive@v4.0.0", Name: "github.com/aquasecurity/go-transitive", - Version: "4.0.0", + Version: "v4.0.0", Relationship: ftypes.RelationshipIndirect, Identifier: ftypes.PkgIdentifier{ UID: "06", @@ -113,7 +113,7 @@ var ( Type: packageurl.TypeGolang, Namespace: "github.com/aquasecurity", Name: "go-transitive", - Version: "4.0.0", + Version: "v4.0.0", }, }, } @@ -646,9 +646,9 @@ func goSinglePathResult(result types.Result) types.Result { result.Type = ftypes.GoModule result.Class = types.ClassLangPkg - // - pkg:golang/github.com/aquasecurity/go-module@1.0.0 - // - pkg:golang/github.com/aquasecurity/go-direct1@2.0.0 - // - pkg:golang/github.com/aquasecurity/go-transitive@4.0.0 + // - pkg:golang/github.com/aquasecurity/go-module@v1.0.0 + // - pkg:golang/github.com/aquasecurity/go-direct1@v2.0.0 + // - pkg:golang/github.com/aquasecurity/go-transitive@v4.0.0 goModule := clonePackage(goModulePackage) goDirect1 := clonePackage(goDirectPackage1) goTransitive := clonePackage(goTransitivePackage) @@ -667,11 +667,11 @@ func goMultiPathResult(result types.Result) types.Result { result.Type = ftypes.GoModule result.Class = types.ClassLangPkg - // - pkg:golang/github.com/aquasecurity/go-module@2.0.0 - // - pkg:golang/github.com/aquasecurity/go-direct1@3.0.0 - // - pkg:golang/github.com/aquasecurity/go-transitive@5.0.0 - // - pkg:golang/github.com/aquasecurity/go-direct2@4.0.0 - // - pkg:golang/github.com/aquasecurity/go-transitive@5.0.0 + // - pkg:golang/github.com/aquasecurity/go-module@v2.0.0 + // - pkg:golang/github.com/aquasecurity/go-direct1@v3.0.0 + // - pkg:golang/github.com/aquasecurity/go-transitive@v5.0.0 + // - pkg:golang/github.com/aquasecurity/go-direct2@v4.0.0 + // - pkg:golang/github.com/aquasecurity/go-transitive@v5.0.0 goModule := clonePackage(goModulePackage) goDirect1 := clonePackage(goDirectPackage1) goDirect2 := clonePackage(goDirectPackage2) From 194d4abb03ba72a6f38287fa7c0e5bef6217c7bc Mon Sep 17 00:00:00 2001 From: simar7 <1254783+simar7@users.noreply.github.com> Date: Thu, 31 Oct 2024 00:37:21 -0600 Subject: [PATCH 063/108] chore(deps): Bump trivy-checks (#7819) --- go.mod | 8 +-- go.sum | 26 +++++---- integration/repo_test.go | 9 --- .../dockerfile-custom-policies.json.golden | 2 +- .../dockerfile-rule-exception.json.golden | 57 ------------------- integration/testdata/dockerfile.json.golden | 2 +- .../dockerfile_file_pattern.json.golden | 2 +- .../fixtures/repo/rule-exception/Dockerfile | 4 -- .../repo/rule-exception/policy/exception.rego | 15 ----- integration/testdata/helm.json.golden | 5 +- .../testdata/helm_testchart.json.golden | 9 +-- .../helm_testchart.overridden.json.golden | 9 +-- 12 files changed, 34 insertions(+), 114 deletions(-) delete mode 100644 integration/testdata/dockerfile-rule-exception.json.golden delete mode 100644 integration/testdata/fixtures/repo/rule-exception/Dockerfile delete mode 100644 integration/testdata/fixtures/repo/rule-exception/policy/exception.rego diff --git a/go.mod b/go.mod index efc9daa926c9..f4a034a8063a 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/aquasecurity/table v1.8.0 github.com/aquasecurity/testdocker v0.0.0-20240730042311-4642e94c7fc8 github.com/aquasecurity/tml v0.6.1 - github.com/aquasecurity/trivy-checks v1.1.0 + github.com/aquasecurity/trivy-checks v1.2.2 github.com/aquasecurity/trivy-db v0.0.0-20240910133327-7e0f4d2ed4c1 github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20241029051843-2606b7e0f0b4 @@ -124,7 +124,7 @@ require ( golang.org/x/mod v0.21.0 golang.org/x/net v0.29.0 golang.org/x/sync v0.8.0 - golang.org/x/term v0.24.0 + golang.org/x/term v0.25.0 golang.org/x/text v0.18.0 golang.org/x/vuln v1.1.3 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 @@ -385,7 +385,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect - golang.org/x/sys v0.25.0 // indirect + golang.org/x/sys v0.26.0 // indirect golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7 // indirect golang.org/x/time v0.6.0 // indirect golang.org/x/tools v0.24.0 // indirect @@ -416,7 +416,7 @@ require ( modernc.org/memory v1.8.0 // indirect modernc.org/strutil v1.2.0 // indirect modernc.org/token v1.1.0 // indirect - mvdan.cc/sh/v3 v3.8.0 // indirect + mvdan.cc/sh/v3 v3.10.0 // indirect oras.land/oras-go v1.2.5 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/kustomize/api v0.17.2 // indirect diff --git a/go.sum b/go.sum index 153c1ab02dea..5eadfbf83818 100644 --- a/go.sum +++ b/go.sum @@ -349,8 +349,8 @@ github.com/aquasecurity/testdocker v0.0.0-20240730042311-4642e94c7fc8 h1:b43UVqY github.com/aquasecurity/testdocker v0.0.0-20240730042311-4642e94c7fc8/go.mod h1:wXA9k3uuaxY3yu7gxrxZDPo/04FEMJtwyecdAlYrEIo= 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-checks v1.1.0 h1:I0tVOK8dG/KHrWsqfGNYp2uD/i0f+yS7Je31F+LIUqQ= -github.com/aquasecurity/trivy-checks v1.1.0/go.mod h1:tVzhU0gajD3GmxKPLn/BHR8ZeUquc5ajQTmAsi0kCCU= +github.com/aquasecurity/trivy-checks v1.2.2 h1:EVHi0gthYzDLfqdAqBBwVGfg2l/gdZ622pIlC9rP+lU= +github.com/aquasecurity/trivy-checks v1.2.2/go.mod h1:TNV0QNVFyBIkt865eO2PtfpubmHt3Ve19Klny//SWIU= github.com/aquasecurity/trivy-db v0.0.0-20240910133327-7e0f4d2ed4c1 h1:G0gnacAORRUqz2Tm5MqivSpldY2GZ74ijhJcMsae+sA= github.com/aquasecurity/trivy-db v0.0.0-20240910133327-7e0f4d2ed4c1/go.mod h1:PYkSRx4dlgFATEt+okGwibvbxVEtqsOdH+vX/saACYE= github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 h1:JVgBIuIYbwG+ekC5lUHUpGJboPYiCcxiz06RCtz8neI= @@ -510,8 +510,8 @@ github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHf github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= -github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0= +github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/csaf-poc/csaf_distribution/v3 v3.0.0 h1:ob9+Fmpff0YWgTP3dYaw7G2hKQ9cegh9l3zksc+q3sM= github.com/csaf-poc/csaf_distribution/v3 v3.0.0/go.mod h1:uilCTiNKivq+6zrDvjtZaUeLk70oe21iwKivo6ILwlQ= github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 h1:2Dx4IHfC1yHWI12AxQDJM1QbRCDfk6M+blLzlZCXdrc= @@ -683,6 +683,8 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91 github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U= github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= +github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= @@ -1204,8 +1206,8 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rubenv/sql-migrate v1.7.0 h1:HtQq1xyTN2ISmQDggnh0c9U3JlP8apWh8YO2jzlXpTI= github.com/rubenv/sql-migrate v1.7.0/go.mod h1:S4wtDEG1CKn+0ShpTtzWhFpHHI5PvCUtiGI+C+Z2THE= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -1703,8 +1705,8 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7 h1:FemxDzfMUcK2f3YY4H+05K9CDzbSVr2+q/JKN45pey0= golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1715,8 +1717,8 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= -golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2124,8 +2126,8 @@ modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= -mvdan.cc/sh/v3 v3.8.0 h1:ZxuJipLZwr/HLbASonmXtcvvC9HXY9d2lXZHnKGjFc8= -mvdan.cc/sh/v3 v3.8.0/go.mod h1:w04623xkgBVo7/IUK89E0g8hBykgEpN0vgOj3RJr6MY= +mvdan.cc/sh/v3 v3.10.0 h1:v9z7N1DLZ7owyLM/SXZQkBSXcwr2IGMm2LY2pmhVXj4= +mvdan.cc/sh/v3 v3.10.0/go.mod h1:z/mSSVyLFGZzqb3ZIKojjyqIx/xbmz/UHdCSv9HmqXY= oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo= oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/integration/repo_test.go b/integration/repo_test.go index 6b4e3a338457..87c27684d3ab 100644 --- a/integration/repo_test.go +++ b/integration/repo_test.go @@ -300,15 +300,6 @@ func TestRepository(t *testing.T) { }, golden: "testdata/dockerfile_file_pattern.json.golden", }, - { - name: "dockerfile with rule exception", - args: args{ - scanner: types.MisconfigScanner, - policyPaths: []string{"testdata/fixtures/repo/rule-exception/policy"}, - input: "testdata/fixtures/repo/rule-exception", - }, - golden: "testdata/dockerfile-rule-exception.json.golden", - }, { name: "dockerfile with custom policies", args: args{ diff --git a/integration/testdata/dockerfile-custom-policies.json.golden b/integration/testdata/dockerfile-custom-policies.json.golden index 2cbda6f16703..64ce52ba64b3 100644 --- a/integration/testdata/dockerfile-custom-policies.json.golden +++ b/integration/testdata/dockerfile-custom-policies.json.golden @@ -21,7 +21,7 @@ "Class": "config", "Type": "dockerfile", "MisconfSummary": { - "Successes": 27, + "Successes": 28, "Failures": 2 }, "Misconfigurations": [ diff --git a/integration/testdata/dockerfile-rule-exception.json.golden b/integration/testdata/dockerfile-rule-exception.json.golden deleted file mode 100644 index eedcb902e145..000000000000 --- a/integration/testdata/dockerfile-rule-exception.json.golden +++ /dev/null @@ -1,57 +0,0 @@ -{ - "SchemaVersion": 2, - "CreatedAt": "2021-08-25T12:20:30.000000005Z", - "ArtifactName": "testdata/fixtures/repo/rule-exception", - "ArtifactType": "repository", - "Metadata": { - "ImageConfig": { - "architecture": "", - "created": "0001-01-01T00:00:00Z", - "os": "", - "rootfs": { - "type": "", - "diff_ids": null - }, - "config": {} - } - }, - "Results": [ - { - "Target": "Dockerfile", - "Class": "config", - "Type": "dockerfile", - "MisconfSummary": { - "Successes": 26, - "Failures": 1 - }, - "Misconfigurations": [ - { - "Type": "Dockerfile Security Check", - "ID": "DS002", - "AVDID": "AVD-DS-0002", - "Title": "Image user should not be 'root'", - "Description": "Running containers with 'root' user can lead to a container escape situation. It is a best practice to run containers as non-root users, which can be done by adding a 'USER' statement to the Dockerfile.", - "Message": "Specify at least 1 USER command in Dockerfile with non-root user as argument", - "Namespace": "builtin.dockerfile.DS002", - "Query": "data.builtin.dockerfile.DS002.deny", - "Resolution": "Add 'USER \u003cnon root user name\u003e' line to the Dockerfile", - "Severity": "HIGH", - "PrimaryURL": "https://avd.aquasec.com/misconfig/ds002", - "References": [ - "https://docs.docker.com/develop/develop-images/dockerfile_best-practices/", - "https://avd.aquasec.com/misconfig/ds002" - ], - "Status": "FAIL", - "Layer": {}, - "CauseMetadata": { - "Provider": "Dockerfile", - "Service": "general", - "Code": { - "Lines": null - } - } - } - ] - } - ] -} diff --git a/integration/testdata/dockerfile.json.golden b/integration/testdata/dockerfile.json.golden index 834b110404c1..fd1fcbfceec8 100644 --- a/integration/testdata/dockerfile.json.golden +++ b/integration/testdata/dockerfile.json.golden @@ -21,7 +21,7 @@ "Class": "config", "Type": "dockerfile", "MisconfSummary": { - "Successes": 26, + "Successes": 27, "Failures": 1 }, "Misconfigurations": [ diff --git a/integration/testdata/dockerfile_file_pattern.json.golden b/integration/testdata/dockerfile_file_pattern.json.golden index ed917023bc4b..adcd916456d5 100644 --- a/integration/testdata/dockerfile_file_pattern.json.golden +++ b/integration/testdata/dockerfile_file_pattern.json.golden @@ -21,7 +21,7 @@ "Class": "config", "Type": "dockerfile", "MisconfSummary": { - "Successes": 26, + "Successes": 27, "Failures": 1 }, "Misconfigurations": [ diff --git a/integration/testdata/fixtures/repo/rule-exception/Dockerfile b/integration/testdata/fixtures/repo/rule-exception/Dockerfile deleted file mode 100644 index 5bcf55456482..000000000000 --- a/integration/testdata/fixtures/repo/rule-exception/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM alpine:3.13 -LABEL user.root="allow" - -HEALTHCHECK NONE \ No newline at end of file diff --git a/integration/testdata/fixtures/repo/rule-exception/policy/exception.rego b/integration/testdata/fixtures/repo/rule-exception/policy/exception.rego deleted file mode 100644 index 224f626021e4..000000000000 --- a/integration/testdata/fixtures/repo/rule-exception/policy/exception.rego +++ /dev/null @@ -1,15 +0,0 @@ -package builtin.dockerfile.DS002 - -exception[rules] { - instruction := input.stages[_][_] - instruction.Cmd == "label" - - key := instruction.Value[i] - i % 2 == 0 - key == "user.root" - - value := instruction.Value[plus(i, 1)] - value == "\"allow\"" - - rules = [""] -} diff --git a/integration/testdata/helm.json.golden b/integration/testdata/helm.json.golden index 8b9ccb11bc9c..36ac8cf19935 100644 --- a/integration/testdata/helm.json.golden +++ b/integration/testdata/helm.json.golden @@ -21,7 +21,7 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 80, + "Successes": 79, "Failures": 14 }, "Misconfigurations": [ @@ -889,10 +889,11 @@ "Namespace": "builtin.kubernetes.KSV117", "Query": "data.builtin.kubernetes.KSV117.deny", "Resolution": "Do not map the container ports to privileged host ports when starting a container.", - "Severity": "HIGH", + "Severity": "MEDIUM", "PrimaryURL": "https://avd.aquasec.com/misconfig/ksv117", "References": [ "https://kubernetes.io/docs/concepts/security/pod-security-standards/", + "https://www.stigviewer.com/stig/kubernetes/2022-12-02/finding/V-242414", "https://avd.aquasec.com/misconfig/ksv117" ], "Status": "FAIL", diff --git a/integration/testdata/helm_testchart.json.golden b/integration/testdata/helm_testchart.json.golden index 886b0a702496..478bcea3b672 100644 --- a/integration/testdata/helm_testchart.json.golden +++ b/integration/testdata/helm_testchart.json.golden @@ -21,7 +21,7 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 90, + "Successes": 89, "Failures": 4 }, "Misconfigurations": [ @@ -317,10 +317,11 @@ "Namespace": "builtin.kubernetes.KSV117", "Query": "data.builtin.kubernetes.KSV117.deny", "Resolution": "Do not map the container ports to privileged host ports when starting a container.", - "Severity": "HIGH", + "Severity": "MEDIUM", "PrimaryURL": "https://avd.aquasec.com/misconfig/ksv117", "References": [ "https://kubernetes.io/docs/concepts/security/pod-security-standards/", + "https://www.stigviewer.com/stig/kubernetes/2022-12-02/finding/V-242414", "https://avd.aquasec.com/misconfig/ksv117" ], "Status": "FAIL", @@ -340,7 +341,7 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 61, + "Successes": 60, "Failures": 0 } }, @@ -349,7 +350,7 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 60, + "Successes": 59, "Failures": 0 } } diff --git a/integration/testdata/helm_testchart.overridden.json.golden b/integration/testdata/helm_testchart.overridden.json.golden index 354cb3cbf786..36710b35734e 100644 --- a/integration/testdata/helm_testchart.overridden.json.golden +++ b/integration/testdata/helm_testchart.overridden.json.golden @@ -21,7 +21,7 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 88, + "Successes": 87, "Failures": 6 }, "Misconfigurations": [ @@ -544,10 +544,11 @@ "Namespace": "builtin.kubernetes.KSV117", "Query": "data.builtin.kubernetes.KSV117.deny", "Resolution": "Do not map the container ports to privileged host ports when starting a container.", - "Severity": "HIGH", + "Severity": "MEDIUM", "PrimaryURL": "https://avd.aquasec.com/misconfig/ksv117", "References": [ "https://kubernetes.io/docs/concepts/security/pod-security-standards/", + "https://www.stigviewer.com/stig/kubernetes/2022-12-02/finding/V-242414", "https://avd.aquasec.com/misconfig/ksv117" ], "Status": "FAIL", @@ -567,7 +568,7 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 61, + "Successes": 60, "Failures": 0 } }, @@ -576,7 +577,7 @@ "Class": "config", "Type": "helm", "MisconfSummary": { - "Successes": 60, + "Successes": 59, "Failures": 0 } } From 7654b2e27e8af2d71586374d3a8c254d46f2416a Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Thu, 31 Oct 2024 15:04:52 +0600 Subject: [PATCH 064/108] docs: add example of creating whitelist of checks (#7821) Signed-off-by: nikpivkin --- docs/docs/configuration/filtering.md | 4 ++-- .../ignore-policies}/advanced.rego | 0 .../ignore-policies}/basic.rego | 0 examples/ignore-policies/whitelist.rego | 13 +++++++++++++ 4 files changed, 15 insertions(+), 2 deletions(-) rename {contrib/example_policy => examples/ignore-policies}/advanced.rego (100%) rename {contrib/example_policy => examples/ignore-policies}/basic.rego (100%) create mode 100644 examples/ignore-policies/whitelist.rego diff --git a/docs/docs/configuration/filtering.md b/docs/docs/configuration/filtering.md index 22fbe2d7a24d..095e5c3bb559 100644 --- a/docs/docs/configuration/filtering.md +++ b/docs/docs/configuration/filtering.md @@ -477,13 +477,13 @@ ignore { ``` ```bash -trivy image --ignore-policy contrib/example_policy/basic.rego centos:7 +trivy image --ignore-policy examples/ignore-policies/basic.rego centos:7 ``` For more advanced use cases, there is a built-in Rego library with helper functions that you can import into your policy using: `import data.lib.trivy`. More info about the helper functions are in the library [here](https://github.com/aquasecurity/trivy/tree/{{ git.tag }}/pkg/result/module.go). -You can find more example checks [here](https://github.com/aquasecurity/trivy/tree/{{ git.tag }}/pkg/result/module.go) +You can create a whitelist of checks using Rego, see the detailed [example](https://github.com/aquasecurity/trivy/tree/{{ git.tag }}/examples/ignore-policies/whitelist.rego). Additional examples are available [here](https://github.com/aquasecurity/trivy/tree/{{ git.tag }}/examples/ignore-policies). ### By Vulnerability Exploitability Exchange (VEX) | Scanner | Supported | diff --git a/contrib/example_policy/advanced.rego b/examples/ignore-policies/advanced.rego similarity index 100% rename from contrib/example_policy/advanced.rego rename to examples/ignore-policies/advanced.rego diff --git a/contrib/example_policy/basic.rego b/examples/ignore-policies/basic.rego similarity index 100% rename from contrib/example_policy/basic.rego rename to examples/ignore-policies/basic.rego diff --git a/examples/ignore-policies/whitelist.rego b/examples/ignore-policies/whitelist.rego new file mode 100644 index 000000000000..51a75c37c72f --- /dev/null +++ b/examples/ignore-policies/whitelist.rego @@ -0,0 +1,13 @@ +package trivy + +import rego.v1 + +allowed_checks := { + "AVD-AWS-0089" +} + +default ignore := false + +ignore if not is_check_allowed + +is_check_allowed if input.AVDID in allowed_checks \ No newline at end of file From dc44946881a9d1c1ef4e655114e9691e828b810d Mon Sep 17 00:00:00 2001 From: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> Date: Thu, 31 Oct 2024 15:41:24 +0600 Subject: [PATCH 065/108] docs(java): add info about supported scopes (#7842) --- docs/docs/coverage/language/java.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/docs/coverage/language/java.md b/docs/docs/coverage/language/java.md index 0ee39bbd3155..30aca897670a 100644 --- a/docs/docs/coverage/language/java.md +++ b/docs/docs/coverage/language/java.md @@ -69,6 +69,9 @@ The vulnerability database will be downloaded anyway. !!! Warning Trivy may skip some dependencies (that were not found on your local machine) when the `--offline-scan` flag is passed. +### supported scopes +Trivy only scans `import`, `compile`, `runtime` and empty [maven scopes][maven-scopes]. Other scopes and `Optional` dependencies are not currently being analyzed. + ### empty dependency version There are cases when Trivy cannot determine the version of dependencies: @@ -128,6 +131,7 @@ Make sure that you have cache[^8] directory to find licenses from `*.pom` depend [maven-invoker-plugin]: https://maven.apache.org/plugins/maven-invoker-plugin/usage.html [maven-central]: https://repo.maven.apache.org/maven2/ [maven-pom-repos]: https://maven.apache.org/settings.html#repositories +[maven-scopes]: https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope [sbt-dependency-lock]: https://stringbean.github.io/sbt-dependency-lock [detection-priority]: ../../scanner/vulnerability.md#detection-priority [version-requirement]: https://maven.apache.org/pom.html#dependency-version-requirement-specification From 5e78b6c12fb5740c12dedeea3d335d48ec2f752b Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Thu, 31 Oct 2024 14:41:02 +0400 Subject: [PATCH 066/108] feat(db): append errors (#7843) Signed-off-by: knqyf263 --- pkg/oci/artifact.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/oci/artifact.go b/pkg/oci/artifact.go index 416952578918..8ed7dcdad03d 100644 --- a/pkg/oci/artifact.go +++ b/pkg/oci/artifact.go @@ -12,6 +12,7 @@ import ( "github.com/google/go-containerregistry/pkg/name" v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/google/go-containerregistry/pkg/v1/remote/transport" + "github.com/hashicorp/go-multierror" "github.com/samber/lo" "golang.org/x/xerrors" @@ -224,6 +225,7 @@ func NewArtifacts(repos []name.Reference, opt types.RegistryOptions, opts ...Opt // Download downloads artifacts until one of them succeeds. // Attempts to download next artifact if the first one fails due to a temporary error. func (a Artifacts) Download(ctx context.Context, dst string, opt DownloadOption) error { + var errs error for i, art := range a { log.InfoContext(ctx, "Downloading artifact...", log.String("repo", art.repository)) err := art.Download(ctx, dst, opt) @@ -239,9 +241,10 @@ func (a Artifacts) Download(ctx context.Context, dst string, opt DownloadOption) if i < len(a)-1 { log.InfoContext(ctx, "Trying to download artifact from other repository...") } + errs = multierror.Append(errs, err) } - return xerrors.New("failed to download artifact from any source") + return xerrors.Errorf("failed to download artifact from any source: %w", errs) } func shouldTryOtherRepo(err error) bool { From 7632625be2c4aa6f18e286baa5466b3dd885dddc Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Thu, 31 Oct 2024 16:08:47 +0400 Subject: [PATCH 067/108] chore: lint `errors.Join` (#7845) Signed-off-by: knqyf263 --- misc/lint/rules.go | 10 ++++++++++ pkg/iac/scanners/cloudformation/parser/parser.go | 11 +++++------ pkg/iac/terraform/block.go | 15 ++++++++------- pkg/report/writer.go | 4 ++-- pkg/vex/repo/repo.go | 3 ++- 5 files changed, 27 insertions(+), 16 deletions(-) diff --git a/misc/lint/rules.go b/misc/lint/rules.go index e477175a244f..4f1b098535ec 100644 --- a/misc/lint/rules.go +++ b/misc/lint/rules.go @@ -20,3 +20,13 @@ func initializeMaps(m dsl.Matcher) { Suggest(`make(map[$key]$value)`). Report(`replace '$$' with 'make(map[$key]$value)`) } + +// While errors.Join from standard library can combine multiple errors, +// we use hashicorp/go-multierror for more user-friendly error outputs. +func errorsJoin(m dsl.Matcher) { + m.Match(`errors.Join($x...)`). + Report("use github.com/hashicorp/go-multierror.Append instead of errors.Join.") + + m.Match(`errors.Join($*args)`). + Report("use github.com/hashicorp/go-multierror.Append instead of errors.Join.") +} diff --git a/pkg/iac/scanners/cloudformation/parser/parser.go b/pkg/iac/scanners/cloudformation/parser/parser.go index 40d0035f6cd2..696dfcf16349 100644 --- a/pkg/iac/scanners/cloudformation/parser/parser.go +++ b/pkg/iac/scanners/cloudformation/parser/parser.go @@ -3,13 +3,13 @@ package parser import ( "context" "encoding/json" - "errors" "fmt" "io" "io/fs" "path/filepath" "strings" + "github.com/hashicorp/go-multierror" "github.com/liamg/jfather" "gopkg.in/yaml.v3" @@ -171,18 +171,17 @@ func (p *Parser) parseParams() error { params := make(Parameters) - var errs []error - + var errs error for _, path := range p.parameterFiles { if parameters, err := p.parseParametersFile(path); err != nil { - errs = append(errs, err) + errs = multierror.Append(errs, err) } else { params.Merge(parameters) } } - if len(errs) != 0 { - return errors.Join(errs...) + if errs != nil { + return errs } params.Merge(p.parameters) diff --git a/pkg/iac/terraform/block.go b/pkg/iac/terraform/block.go index dd6d8446c124..348f938d4559 100644 --- a/pkg/iac/terraform/block.go +++ b/pkg/iac/terraform/block.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/google/uuid" + "github.com/hashicorp/go-multierror" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" "github.com/zclconf/go-cty/cty" @@ -600,14 +601,14 @@ func (b *Block) IsNotNil() bool { func (b *Block) ExpandBlock() error { var ( expanded []*Block - errs []error + errs error ) for _, child := range b.childBlocks { if child.Type() == "dynamic" { blocks, err := child.expandDynamic() if err != nil { - errs = append(errs, err) + errs = multierror.Append(errs, err) continue } expanded = append(expanded, blocks...) @@ -618,7 +619,7 @@ func (b *Block) ExpandBlock() error { b.injectBlock(block) } - return errors.Join(errs...) + return errs } func (b *Block) expandDynamic() ([]*Block, error) { @@ -638,7 +639,7 @@ func (b *Block) expandDynamic() ([]*Block, error) { var ( expanded []*Block - errs []error + errs error ) forEachVal.ForEachElement(func(key, val cty.Value) (stop bool) { @@ -648,7 +649,7 @@ func (b *Block) expandDynamic() ([]*Block, error) { iteratorName, err := b.iteratorName(realBlockType) if err != nil { - errs = append(errs, err) + errs = multierror.Append(errs, err) return } @@ -664,7 +665,7 @@ func (b *Block) expandDynamic() ([]*Block, error) { inherited.hclBlock.Labels = []string{} inherited.hclBlock.Type = realBlockType if err := inherited.ExpandBlock(); err != nil { - errs = append(errs, err) + errs = multierror.Append(errs, err) return } expanded = append(expanded, inherited) @@ -676,7 +677,7 @@ func (b *Block) expandDynamic() ([]*Block, error) { b.markExpanded() } - return expanded, errors.Join(errs...) + return expanded, errs } func (b *Block) validateForEach() (cty.Value, error) { diff --git a/pkg/report/writer.go b/pkg/report/writer.go index a5b7ae231599..f25d579d66ef 100644 --- a/pkg/report/writer.go +++ b/pkg/report/writer.go @@ -2,10 +2,10 @@ package report import ( "context" - "errors" "io" "strings" + "github.com/hashicorp/go-multierror" "golang.org/x/xerrors" cr "github.com/aquasecurity/trivy/pkg/compliance/report" @@ -32,7 +32,7 @@ func Write(ctx context.Context, report types.Report, option flag.Options) (err e } defer func() { if cerr := cleanup(); cerr != nil { - err = errors.Join(err, cerr) + err = multierror.Append(err, cerr) } }() diff --git a/pkg/vex/repo/repo.go b/pkg/vex/repo/repo.go index 433b8224c20e..b9637c229ee9 100644 --- a/pkg/vex/repo/repo.go +++ b/pkg/vex/repo/repo.go @@ -11,6 +11,7 @@ import ( "time" "github.com/hashicorp/go-getter" + "github.com/hashicorp/go-multierror" "github.com/samber/lo" "golang.org/x/xerrors" @@ -256,7 +257,7 @@ func (r *Repository) download(ctx context.Context, ver Version, dst string, opts etag = etags[loc.URL] // Keep the old ETag // Update last updated time so that Trivy will not try to download the same URL soon case err != nil: - errs = errors.Join(errs, err) + errs = multierror.Append(errs, err) continue // Try the next location default: // Successfully downloaded From efec32669b79aba5ae61771e2d655e45e313ac2f Mon Sep 17 00:00:00 2001 From: Aqua Security automated builds <54269356+aqua-bot@users.noreply.github.com> Date: Fri, 1 Nov 2024 06:01:54 +0200 Subject: [PATCH 068/108] release: v0.57.0 [main] (#7710) --- .release-please-manifest.json | 2 +- CHANGELOG.md | 46 +++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 208746a81be3..0fd1f968a6d2 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1 +1 @@ -{".":"0.56.0"} +{".":"0.57.0"} diff --git a/CHANGELOG.md b/CHANGELOG.md index afc44ba7a956..f4bad92b11d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,51 @@ # Changelog +## [0.57.0](https://github.com/aquasecurity/trivy/compare/v0.56.0...v0.57.0) (2024-10-31) + + +### ⚠ BREAKING CHANGES + +* **k8s:** support k8s multi container ([#7444](https://github.com/aquasecurity/trivy/issues/7444)) + +### Features + +* add end of life date for Ubuntu 24.10 ([#7787](https://github.com/aquasecurity/trivy/issues/7787)) ([ad3c09e](https://github.com/aquasecurity/trivy/commit/ad3c09e006e134f3c5b879ffc34ce9895a8c860f)) +* **cli:** add `trivy auth` ([#7664](https://github.com/aquasecurity/trivy/issues/7664)) ([27117f8](https://github.com/aquasecurity/trivy/commit/27117f81d52483c3ceec56fe56ac298e242fbc9a)) +* **cli:** error out when ignore file cannot be found ([#7624](https://github.com/aquasecurity/trivy/issues/7624)) ([cb0b3a9](https://github.com/aquasecurity/trivy/commit/cb0b3a9279b31810ecd686a385e5140e567ce86f)) +* **cli:** rename `trivy auth` to `trivy registry` ([#7727](https://github.com/aquasecurity/trivy/issues/7727)) ([633a7ab](https://github.com/aquasecurity/trivy/commit/633a7abeea4287899392a24f2705f96dfeb7e312)) +* **cyclonedx:** add file checksums to `CycloneDX` reports ([#7507](https://github.com/aquasecurity/trivy/issues/7507)) ([c225883](https://github.com/aquasecurity/trivy/commit/c225883649f58128a99fa2c1cef327d0e57940be)) +* **db:** append errors ([#7843](https://github.com/aquasecurity/trivy/issues/7843)) ([5e78b6c](https://github.com/aquasecurity/trivy/commit/5e78b6c12fb5740c12dedeea3d335d48ec2f752b)) +* **misconf:** export unresolvable field of IaC types to Rego ([#7765](https://github.com/aquasecurity/trivy/issues/7765)) ([9514148](https://github.com/aquasecurity/trivy/commit/9514148767865baddd73a49245385574927f7a74)) +* **misconf:** public network support for Azure Storage Account ([#7601](https://github.com/aquasecurity/trivy/issues/7601)) ([ad91412](https://github.com/aquasecurity/trivy/commit/ad914123c4d203af1e1da6b7e2d3e49d9d3831d8)) +* **misconf:** Show misconfig ID in output ([#7762](https://github.com/aquasecurity/trivy/issues/7762)) ([f75c0d1](https://github.com/aquasecurity/trivy/commit/f75c0d1f0069d4856cb4826d6049f32c5b9409d9)) +* **misconf:** ssl_mode support for GCP SQL DB instance ([#7564](https://github.com/aquasecurity/trivy/issues/7564)) ([2eaa17e](https://github.com/aquasecurity/trivy/commit/2eaa17e0717940b27a79050e2efd9213b71178c9)) +* **parser:** ignore white space in pom.xml files ([#7747](https://github.com/aquasecurity/trivy/issues/7747)) ([a7baa93](https://github.com/aquasecurity/trivy/commit/a7baa93b00b8636aa097e64cdb8eed97dbd68511)) +* **report:** update gitlab template to populate operating_system value ([#7735](https://github.com/aquasecurity/trivy/issues/7735)) ([c0d79fa](https://github.com/aquasecurity/trivy/commit/c0d79fa09e645f3a3dbff878e393b8631fb17b64)) + + +### Bug Fixes + +* **cli:** `clean --all` deletes only relevant dirs ([#7704](https://github.com/aquasecurity/trivy/issues/7704)) ([672e886](https://github.com/aquasecurity/trivy/commit/672e886aed152ae0f09a16941706746f3053ca94)) +* **cli:** add config name to skip-policy-update alias ([#7820](https://github.com/aquasecurity/trivy/issues/7820)) ([b661d68](https://github.com/aquasecurity/trivy/commit/b661d680ff0372c8e4beea0db13bf69d6a2203a8)) +* **db:** fix javadb downloading error handling ([#7642](https://github.com/aquasecurity/trivy/issues/7642)) ([2c87f0c](https://github.com/aquasecurity/trivy/commit/2c87f0cb794acd77446a273582ba1a45b9f18980)) +* enable usestdlibvars linter ([#7770](https://github.com/aquasecurity/trivy/issues/7770)) ([57e24aa](https://github.com/aquasecurity/trivy/commit/57e24aa85382f749df7f673e241caaf3fcbb45cb)) +* **go:** Do not trim v prefix from versions in Go Mod Analyzer ([#7733](https://github.com/aquasecurity/trivy/issues/7733)) ([e872ec0](https://github.com/aquasecurity/trivy/commit/e872ec006c0745a5a142728af0096c6d6bb9ddf3)) +* **helm:** properly handle multiple archived dependencies ([#7782](https://github.com/aquasecurity/trivy/issues/7782)) ([6fab88d](https://github.com/aquasecurity/trivy/commit/6fab88dd56c257ef2cc63b617c2a5decb1c4cf98)) +* **java:** correctly inherit `version` and `scope` from upper/root `depManagement` and `dependencies` into parents ([#7541](https://github.com/aquasecurity/trivy/issues/7541)) ([778df82](https://github.com/aquasecurity/trivy/commit/778df828eaad9827cb833c6285058a33aa2b83ca)) +* **k8s:** skip resources without misconfigs ([#7797](https://github.com/aquasecurity/trivy/issues/7797)) ([7882776](https://github.com/aquasecurity/trivy/commit/78827768a612ab305bf9c55409ce76d6774302a5)) +* **k8s:** support k8s multi container ([#7444](https://github.com/aquasecurity/trivy/issues/7444)) ([c434775](https://github.com/aquasecurity/trivy/commit/c4347759234dcb5f372b07f92fb4230ef391d710)) +* **k8s:** support kubernetes v1.31 ([#7810](https://github.com/aquasecurity/trivy/issues/7810)) ([7a4f4d8](https://github.com/aquasecurity/trivy/commit/7a4f4d8b12996687f3095a2042cdf2f5985332c9)) +* **license:** fix license normalization for Universal Permissive License ([#7766](https://github.com/aquasecurity/trivy/issues/7766)) ([f6acdf7](https://github.com/aquasecurity/trivy/commit/f6acdf713991f8ffdbe765178fcb8a9cde433cba)) +* **misconf:** change default ACL of digitalocean_spaces_bucket to private ([#7577](https://github.com/aquasecurity/trivy/issues/7577)) ([9da84f5](https://github.com/aquasecurity/trivy/commit/9da84f54fadbe6ad0d73983952e945ed63b666f3)) +* **misconf:** check if property is not nil before conversion ([#7578](https://github.com/aquasecurity/trivy/issues/7578)) ([c8c14d3](https://github.com/aquasecurity/trivy/commit/c8c14d36245623019f29d258f813d2325f7490f7)) +* **misconf:** fix for Azure Storage Account network acls adaptation ([#7602](https://github.com/aquasecurity/trivy/issues/7602)) ([35fd018](https://github.com/aquasecurity/trivy/commit/35fd018ae7ad86823f114f0ac2f1376726aee444)) +* **misconf:** properly expand dynamic blocks ([#7612](https://github.com/aquasecurity/trivy/issues/7612)) ([8d5dbc9](https://github.com/aquasecurity/trivy/commit/8d5dbc9fec3569b22ed81a03c40eaf732768718b)) +* **redhat:** include arch in PURL qualifiers ([#7654](https://github.com/aquasecurity/trivy/issues/7654)) ([a585e95](https://github.com/aquasecurity/trivy/commit/a585e95f3398631d9ad10505c5ff642fde21aef7)) +* **repo:** `git clone` output to Stderr ([#7561](https://github.com/aquasecurity/trivy/issues/7561)) ([fdf203c](https://github.com/aquasecurity/trivy/commit/fdf203cd209aeb40f454bd12d121a54d6ed7a542)) +* **report:** Fix invalid URI in SARIF report ([#7645](https://github.com/aquasecurity/trivy/issues/7645)) ([015bb88](https://github.com/aquasecurity/trivy/commit/015bb885ac414b91201fa9791eead395d878149c)) +* **sbom:** add options for DBs in private registries ([#7660](https://github.com/aquasecurity/trivy/issues/7660)) ([1f2e91b](https://github.com/aquasecurity/trivy/commit/1f2e91b02b3606dd11963002a8cfac7962f3478f)) +* **sbom:** use `Annotation` instead of `AttributionTexts` for `SPDX` formats ([#7811](https://github.com/aquasecurity/trivy/issues/7811)) ([f2bb9c6](https://github.com/aquasecurity/trivy/commit/f2bb9c6227743dd61f44eb591d4b15192fe110c6)) + ## [0.56.0](https://github.com/aquasecurity/trivy/compare/v0.55.0...v0.56.0) (2024-10-03) From 4da59bd7b621366b05480cb84ee01786bc95f2fd Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Mon, 4 Nov 2024 10:01:05 +0400 Subject: [PATCH 069/108] chore: bump golangci-lint to v1.61.0 (#7853) Signed-off-by: knqyf263 --- .github/workflows/test.yaml | 2 +- .golangci.yaml | 1 + magefiles/magefile.go | 2 +- pkg/fanal/walker/walk_test.go | 19 ++++++++++++------- pkg/plugin/index.go | 2 +- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 05edadf93331..c2af5eb66bbb 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -42,7 +42,7 @@ jobs: id: lint uses: golangci/golangci-lint-action@v6.0.1 with: - version: v1.59 + version: v1.61 args: --verbose --out-format=line-number if: matrix.operating-system == 'ubuntu-latest' diff --git a/.golangci.yaml b/.golangci.yaml index 40bfa36e1a64..b6dbc299be20 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -68,6 +68,7 @@ linters-settings: excludes: - G101 - G114 + - G115 - G204 - G304 - G402 diff --git a/magefiles/magefile.go b/magefiles/magefile.go index f70491bce2e8..5b08e907f0c0 100644 --- a/magefiles/magefile.go +++ b/magefiles/magefile.go @@ -77,7 +77,7 @@ func (Tool) Wire() error { // GolangciLint installs golangci-lint func (t Tool) GolangciLint() error { - const version = "v1.59.1" + const version = "v1.61.0" bin := filepath.Join(GOBIN, "golangci-lint") if exists(bin) && t.matchGolangciLintVersion(bin, version) { return nil diff --git a/pkg/fanal/walker/walk_test.go b/pkg/fanal/walker/walk_test.go index 09d55527c0fe..3c066529393f 100644 --- a/pkg/fanal/walker/walk_test.go +++ b/pkg/fanal/walker/walk_test.go @@ -1,7 +1,6 @@ package walker_test import ( - "fmt" "path/filepath" "testing" @@ -42,8 +41,11 @@ func TestSkipFile(t *testing.T) { }, }, { - name: "multiple skip files", - skipFiles: []string{"/etc/*/*", "/var/log/*.txt"}, + name: "multiple skip files", + skipFiles: []string{ + "/etc/*/*", + "/var/log/*.txt", + }, wants: map[string]bool{ "/etc/foo": false, "/etc/foo/bar": true, @@ -65,7 +67,7 @@ func TestSkipFile(t *testing.T) { for file, want := range tt.wants { file = filepath.ToSlash(filepath.Clean(file)) got := utils.SkipPath(file, utils.CleanSkipPaths(tt.skipFiles)) - assert.Equal(t, want, got, fmt.Sprintf("skipFiles: %s, file: %s", tt.skipFiles, file)) + assert.Equal(t, want, got, "skipFiles: %s, file: %s", tt.skipFiles, file) } }) } @@ -108,8 +110,11 @@ func TestSkipDir(t *testing.T) { }, }, { - name: "multiple dirs", - skipDirs: []string{"/etc/*/*", "/var/log/*"}, + name: "multiple dirs", + skipDirs: []string{ + "/etc/*/*", + "/var/log/*", + }, wants: map[string]bool{ "/etc/foo": false, "/etc/foo/bar": true, @@ -139,7 +144,7 @@ func TestSkipDir(t *testing.T) { for dir, want := range tt.wants { dir = filepath.ToSlash(filepath.Clean(dir)) got := utils.SkipPath(dir, utils.CleanSkipPaths(tt.skipDirs)) - assert.Equal(t, want, got, fmt.Sprintf("defaultSkipDirs: %s, dir: %s", tt.skipDirs, dir)) + assert.Equal(t, want, got, "defaultSkipDirs: %s, dir: %s", tt.skipDirs, dir) } }) } diff --git a/pkg/plugin/index.go b/pkg/plugin/index.go index 57c6f0260877..82e4cb076356 100644 --- a/pkg/plugin/index.go +++ b/pkg/plugin/index.go @@ -61,7 +61,7 @@ func (m *Manager) Search(ctx context.Context, keyword string) error { } } - if _, err = fmt.Fprintf(m.w, buf.String()); err != nil { + if _, err = fmt.Fprint(m.w, buf.String()); err != nil { return err } From a9b557d9ddb863cee4bb60130f62521875356796 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 10:11:06 +0400 Subject: [PATCH 070/108] chore(deps): bump the github-actions group across 1 directory with 2 updates (#7854) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/reusable-release.yaml | 2 +- .github/workflows/test.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/reusable-release.yaml b/.github/workflows/reusable-release.yaml index 0f7064ee18e1..54fa746f768f 100644 --- a/.github/workflows/reusable-release.yaml +++ b/.github/workflows/reusable-release.yaml @@ -28,7 +28,7 @@ jobs: contents: read # Not required for public repositories, but for clarity steps: - name: Cosign install - uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 + uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da - name: Set up QEMU uses: docker/setup-qemu-action@v3 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index c2af5eb66bbb..1113a6a3a894 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -40,7 +40,7 @@ jobs: - name: Lint id: lint - uses: golangci/golangci-lint-action@v6.0.1 + uses: golangci/golangci-lint-action@v6.1.1 with: version: v1.61 args: --verbose --out-format=line-number From a6a45b346aede67af44e96c7b2b9abad1826890c Mon Sep 17 00:00:00 2001 From: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> Date: Mon, 4 Nov 2024 12:14:05 +0600 Subject: [PATCH 071/108] test: save `containerd` image into archive and use in tests (#7816) --- .github/workflows/cache-test-images.yaml | 4 +- .github/workflows/test.yaml | 6 +- magefiles/fixture.go | 55 +++++++++++++------ pkg/fanal/test/integration/containerd_test.go | 7 ++- 4 files changed, 49 insertions(+), 23 deletions(-) diff --git a/.github/workflows/cache-test-images.yaml b/.github/workflows/cache-test-images.yaml index 4b73cc2fb5bc..dd3a76d10638 100644 --- a/.github/workflows/cache-test-images.yaml +++ b/.github/workflows/cache-test-images.yaml @@ -29,7 +29,7 @@ jobs: run: | source integration/testimages.ini IMAGE_LIST=$(skopeo list-tags docker://$TEST_IMAGES) - DIGEST=$(echo "$IMAGE_LIST" | sha256sum | cut -d' ' -f1) + DIGEST=$(echo "$IMAGE_LIST" | jq '.Tags += ["containerd"] | .Tags | sort' | sha256sum | cut -d' ' -f1) echo "digest=$DIGEST" >> $GITHUB_OUTPUT ## We need to work with test image cache only for main branch @@ -70,7 +70,7 @@ jobs: run: | source integration/testimages.ini IMAGE_LIST=$(skopeo list-tags docker://$TEST_VM_IMAGES) - DIGEST=$(echo "$IMAGE_LIST" | sha256sum | cut -d' ' -f1) + DIGEST=$(echo "$IMAGE_LIST" | jq '.Tags | sort' | sha256sum | cut -d' ' -f1) echo "digest=$DIGEST" >> $GITHUB_OUTPUT ## We need to work with test VM image cache only for main branch diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 1113a6a3a894..9c6c2f10ffba 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -93,7 +93,7 @@ jobs: run: | source integration/testimages.ini IMAGE_LIST=$(skopeo list-tags docker://$TEST_IMAGES) - DIGEST=$(echo "$IMAGE_LIST" | sha256sum | cut -d' ' -f1) + DIGEST=$(echo "$IMAGE_LIST" | jq '.Tags += ["containerd"] | .Tags | sort' | sha256sum | cut -d' ' -f1) echo "digest=$DIGEST" >> $GITHUB_OUTPUT - name: Restore test images from cache @@ -151,7 +151,7 @@ jobs: run: | source integration/testimages.ini IMAGE_LIST=$(skopeo list-tags docker://$TEST_IMAGES) - DIGEST=$(echo "$IMAGE_LIST" | sha256sum | cut -d' ' -f1) + DIGEST=$(echo "$IMAGE_LIST" | jq '.Tags += ["containerd"] | .Tags | sort' | sha256sum | cut -d' ' -f1) echo "digest=$DIGEST" >> $GITHUB_OUTPUT - name: Restore test images from cache @@ -190,7 +190,7 @@ jobs: run: | source integration/testimages.ini IMAGE_LIST=$(skopeo list-tags docker://$TEST_VM_IMAGES) - DIGEST=$(echo "$IMAGE_LIST" | sha256sum | cut -d' ' -f1) + DIGEST=$(echo "$IMAGE_LIST" | jq '.Tags | sort' | sha256sum | cut -d' ' -f1) echo "digest=$DIGEST" >> $GITHUB_OUTPUT - name: Restore test VM images from cache diff --git a/magefiles/fixture.go b/magefiles/fixture.go index 0112a8421133..e7a2c395157e 100644 --- a/magefiles/fixture.go +++ b/magefiles/fixture.go @@ -14,9 +14,11 @@ import ( "github.com/aquasecurity/trivy/internal/testutil" ) +const dir = "integration/testdata/fixtures/images/" + func fixtureContainerImages() error { var testImages = testutil.ImageName("", "", "") - const dir = "integration/testdata/fixtures/images/" + if err := os.MkdirAll(dir, 0750); err != nil { return err } @@ -24,26 +26,45 @@ func fixtureContainerImages() error { if err != nil { return err } + // Save all tags for trivy-test-images for _, tag := range tags { - fileName := tag + ".tar.gz" - filePath := filepath.Join(dir, fileName) - if exists(filePath) { - continue - } - fmt.Printf("Downloading %s...\n", tag) - imgName := fmt.Sprintf("%s:%s", testImages, tag) - img, err := crane.Pull(imgName) - if err != nil { - return err - } - tarPath := strings.TrimSuffix(filePath, ".gz") - if err = crane.Save(img, imgName, tarPath); err != nil { - return err - } - if err = sh.Run("gzip", tarPath); err != nil { + if err := saveImage("", tag); err != nil { return err } } + + // Save trivy-test-images/containerd image + if err := saveImage("containerd", "latest"); err != nil { + return err + } + return nil +} + +func saveImage(subpath, tag string) error { + fileName := tag + ".tar.gz" + imgName := testutil.ImageName("", tag, "") + if subpath != "" { + fileName = subpath + ".tar.gz" + imgName = testutil.ImageName(subpath, "", "") + } + filePath := filepath.Join(dir, fileName) + if exists(filePath) { + return nil + } + fmt.Printf("Downloading %s...\n", imgName) + + img, err := crane.Pull(imgName) + if err != nil { + return err + } + tarPath := strings.TrimSuffix(filePath, ".gz") + if err = crane.Save(img, imgName, tarPath); err != nil { + return err + } + if err = sh.Run("gzip", tarPath); err != nil { + return err + } + return nil } diff --git a/pkg/fanal/test/integration/containerd_test.go b/pkg/fanal/test/integration/containerd_test.go index 568af60528ea..420949cd42d3 100644 --- a/pkg/fanal/test/integration/containerd_test.go +++ b/pkg/fanal/test/integration/containerd_test.go @@ -76,9 +76,14 @@ func setupContainerd(t *testing.T, ctx context.Context, namespace string) *conta func startContainerd(t *testing.T, ctx context.Context, hostPath string) { t.Helper() t.Setenv("TESTCONTAINERS_RYUK_DISABLED", "true") + + // Load `containerd` image from tar file to avoid fetching it from remote registry + cli := testutil.NewDockerClient(t) + loadedImage := cli.ImageLoad(t, ctx, "../../../../integration/testdata/fixtures/images/containerd.tar.gz") + req := testcontainers.ContainerRequest{ Name: "containerd", - Image: testutil.ImageName("containerd", "latest", ""), + Image: loadedImage, Entrypoint: []string{ "/bin/sh", "-c", From 2529b58d4db8e73af977bbb601f966d9821c2367 Mon Sep 17 00:00:00 2001 From: Yeates Date: Tue, 5 Nov 2024 13:39:47 +0800 Subject: [PATCH 072/108] refactor(secret): optimize performance by moving ToLower operation outside loop (#7862) --- pkg/fanal/secret/scanner.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/fanal/secret/scanner.go b/pkg/fanal/secret/scanner.go index 33a3806f41b4..01283ea42a46 100644 --- a/pkg/fanal/secret/scanner.go +++ b/pkg/fanal/secret/scanner.go @@ -175,9 +175,9 @@ func (r *Rule) MatchKeywords(content []byte) bool { if len(r.Keywords) == 0 { return true } - + var contentLower = bytes.ToLower(content) for _, kw := range r.Keywords { - if bytes.Contains(bytes.ToLower(content), []byte(strings.ToLower(kw))) { + if bytes.Contains(contentLower, []byte(strings.ToLower(kw))) { return true } } From ed2288f1e7b1195db179191e51a1952272c2c064 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 05:40:34 +0000 Subject: [PATCH 073/108] chore(deps): bump github.com/golang-jwt/jwt/v4 from 4.5.0 to 4.5.1 (#7868) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index f4a034a8063a..fced18a28f4d 100644 --- a/go.mod +++ b/go.mod @@ -247,7 +247,7 @@ require ( github.com/goccy/go-yaml v1.9.5 // indirect github.com/gofrs/uuid v4.3.1+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/golang/snappy v0.0.4 // indirect diff --git a/go.sum b/go.sum index 5eadfbf83818..f116a5db3505 100644 --- a/go.sum +++ b/go.sum @@ -710,8 +710,9 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= +github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= From b1c7f5516fc39c6cbb76cbeae5c8677ccc9ce5dd Mon Sep 17 00:00:00 2001 From: afdesk Date: Wed, 6 Nov 2024 04:58:01 +0600 Subject: [PATCH 074/108] feat(k8s): add default commands for unknown platform (#7863) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fced18a28f4d..c7bb5f608c47 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/aquasecurity/trivy-checks v1.2.2 github.com/aquasecurity/trivy-db v0.0.0-20240910133327-7e0f4d2ed4c1 github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 - github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20241029051843-2606b7e0f0b4 + github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20241101182546-89bffc3932bc github.com/aws/aws-sdk-go-v2 v1.31.0 github.com/aws/aws-sdk-go-v2/config v1.27.38 github.com/aws/aws-sdk-go-v2/credentials v1.17.36 diff --git a/go.sum b/go.sum index f116a5db3505..991cad7e2929 100644 --- a/go.sum +++ b/go.sum @@ -355,8 +355,8 @@ github.com/aquasecurity/trivy-db v0.0.0-20240910133327-7e0f4d2ed4c1 h1:G0gnacAOR github.com/aquasecurity/trivy-db v0.0.0-20240910133327-7e0f4d2ed4c1/go.mod h1:PYkSRx4dlgFATEt+okGwibvbxVEtqsOdH+vX/saACYE= github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 h1:JVgBIuIYbwG+ekC5lUHUpGJboPYiCcxiz06RCtz8neI= github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48/go.mod h1:Ldya37FLi0e/5Cjq2T5Bty7cFkzUDwTcPeQua+2M8i8= -github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20241029051843-2606b7e0f0b4 h1:i0Z0JS4xtMAcBVOpYSciS7slmIBi1SmjT6garbrJtcA= -github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20241029051843-2606b7e0f0b4/go.mod h1:ctlibFXOQyjWybeVVQI6NLG6GJoPWZJ4cIirQ/wPCQs= +github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20241101182546-89bffc3932bc h1:/mFBYIK9RY+L8s1CIbQbJ5B3v0YmoDSu5eAzavvMa+Y= +github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20241101182546-89bffc3932bc/go.mod h1:ctlibFXOQyjWybeVVQI6NLG6GJoPWZJ4cIirQ/wPCQs= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= From eda4d7660d8908705bc08a6edc55d8144d02806a Mon Sep 17 00:00:00 2001 From: Ben Foster Date: Wed, 6 Nov 2024 01:31:23 -0500 Subject: [PATCH 075/108] fix: Improve version comparisons when build identifiers are present (#7873) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c7bb5f608c47..93acd5cf9334 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( 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 - github.com/aquasecurity/go-version v0.0.0-20240603093900-cf8a8d29271d + github.com/aquasecurity/go-version v0.0.0-20241105054539-1951e80d786f github.com/aquasecurity/table v1.8.0 github.com/aquasecurity/testdocker v0.0.0-20240730042311-4642e94c7fc8 github.com/aquasecurity/tml v0.6.1 diff --git a/go.sum b/go.sum index 991cad7e2929..f2112b2721ef 100644 --- a/go.sum +++ b/go.sum @@ -341,8 +341,8 @@ github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 h1: github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46/go.mod h1:olhPNdiiAAMiSujemd1O/sc6GcyePr23f/6uGKtthNg= github.com/aquasecurity/go-version v0.0.0-20201107203531-5e48ac5d022a/go.mod h1:9Beu8XsUNNfzml7WBf3QmyPToP1wm1Gj/Vc5UJKqTzU= github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492/go.mod h1:9Beu8XsUNNfzml7WBf3QmyPToP1wm1Gj/Vc5UJKqTzU= -github.com/aquasecurity/go-version v0.0.0-20240603093900-cf8a8d29271d h1:4zour5Sh9chOg+IqIinIcJ3qtr3cIf8FdFY6aArlXBw= -github.com/aquasecurity/go-version v0.0.0-20240603093900-cf8a8d29271d/go.mod h1:1cPOp4BaQZ1G2F5fnw4dFz6pkOyXJI9KTuak8ghIl3U= +github.com/aquasecurity/go-version v0.0.0-20241105054539-1951e80d786f h1:6mwfszC0VohA3NF75EX8pPStmmL0spZnTlkLp83M69c= +github.com/aquasecurity/go-version v0.0.0-20241105054539-1951e80d786f/go.mod h1:1cPOp4BaQZ1G2F5fnw4dFz6pkOyXJI9KTuak8ghIl3U= github.com/aquasecurity/table v1.8.0 h1:9ntpSwrUfjrM6/YviArlx/ZBGd6ix8W+MtojQcM7tv0= github.com/aquasecurity/table v1.8.0/go.mod h1:eqOmvjjB7AhXFgFqpJUEE/ietg7RrMSJZXyTN8E/wZw= github.com/aquasecurity/testdocker v0.0.0-20240730042311-4642e94c7fc8 h1:b43UVqYjz7qDqK+cVOtF2Lk6CxjytYItP6Pgf3wGsNE= From 6e3252b1d314ddcb4904e6f98abb8ac1e3d3cc94 Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Wed, 6 Nov 2024 11:32:43 +0400 Subject: [PATCH 076/108] chore: bump containerd to v2.0.0 (#7875) Signed-off-by: knqyf263 --- go.mod | 78 ++++---- go.sum | 180 ++++++++++-------- pkg/fanal/image/daemon/containerd.go | 56 +++--- pkg/fanal/test/integration/containerd_test.go | 19 +- 4 files changed, 180 insertions(+), 153 deletions(-) diff --git a/go.mod b/go.mod index 93acd5cf9334..566d5069c0b9 100644 --- a/go.mod +++ b/go.mod @@ -41,8 +41,10 @@ require ( github.com/bmatcuk/doublestar/v4 v4.6.1 github.com/cenkalti/backoff/v4 v4.3.0 github.com/cheggaaa/pb/v3 v3.1.5 - github.com/containerd/containerd v1.7.22 + github.com/containerd/containerd/v2 v2.0.0 + github.com/containerd/platforms v1.0.0-rc.0 github.com/csaf-poc/csaf_distribution/v3 v3.0.0 + github.com/distribution/reference v0.6.0 github.com/docker/cli v27.2.1+incompatible github.com/docker/docker v27.3.1+incompatible github.com/docker/go-connections v0.5.0 @@ -119,16 +121,16 @@ require ( github.com/zclconf/go-cty v1.15.0 github.com/zclconf/go-cty-yaml v1.0.3 go.etcd.io/bbolt v1.3.11 - golang.org/x/crypto v0.27.0 - golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + golang.org/x/crypto v0.28.0 + golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 // indirect golang.org/x/mod v0.21.0 - golang.org/x/net v0.29.0 + golang.org/x/net v0.30.0 golang.org/x/sync v0.8.0 golang.org/x/term v0.25.0 - golang.org/x/text v0.18.0 + golang.org/x/text v0.19.0 golang.org/x/vuln v1.1.3 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 - google.golang.org/protobuf v1.34.2 + google.golang.org/protobuf v1.35.1 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.16.1 k8s.io/api v0.31.2 @@ -139,12 +141,12 @@ require ( require ( cloud.google.com/go v0.112.1 // indirect - cloud.google.com/go/compute/metadata v0.3.0 // indirect + cloud.google.com/go/compute/metadata v0.5.0 // indirect cloud.google.com/go/iam v1.1.6 // indirect cloud.google.com/go/storage v1.39.1 // indirect dario.cat/mergo v1.0.1 // indirect - github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect - github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 // indirect + github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect + github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect @@ -162,7 +164,7 @@ require ( github.com/Masterminds/semver/v3 v3.3.0 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/Microsoft/hcsshim v0.12.0 // indirect + github.com/Microsoft/hcsshim v0.12.9 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect github.com/VividCortex/ewma v1.2.0 // indirect @@ -191,29 +193,29 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect github.com/cloudflare/circl v1.3.8 // indirect - github.com/containerd/cgroups/v3 v3.0.2 // indirect - github.com/containerd/containerd/api v1.7.19 // indirect - github.com/containerd/continuity v0.4.3 // indirect - github.com/containerd/errdefs v0.1.0 // indirect + github.com/containerd/cgroups/v3 v3.0.3 // indirect + github.com/containerd/containerd v1.7.23 // indirect + github.com/containerd/containerd/api v1.8.0 // indirect + github.com/containerd/continuity v0.4.4 // indirect + github.com/containerd/errdefs v1.0.0 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/fifo v1.1.0 // indirect github.com/containerd/log v0.1.0 // indirect - github.com/containerd/platforms v0.2.1 // indirect + github.com/containerd/plugin v1.0.0 // indirect github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect - github.com/containerd/ttrpc v1.2.5 // indirect - github.com/containerd/typeurl/v2 v2.2.0 // indirect + github.com/containerd/ttrpc v1.2.6 // indirect + github.com/containerd/typeurl/v2 v2.2.2 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 // indirect github.com/cyphar/filepath-securejoin v0.3.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect - github.com/distribution/reference v0.6.0 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.8.2 // indirect - github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 // indirect @@ -281,7 +283,7 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/compress v1.17.11 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/letsencrypt/boulder v0.0.0-20231026200631-000cd05d5491 // indirect @@ -303,7 +305,7 @@ require ( github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/spdystream v0.4.0 // indirect github.com/moby/sys/mountinfo v0.7.2 // indirect - github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect github.com/moby/sys/signal v0.7.1 // indirect github.com/moby/sys/user v0.3.0 // indirect github.com/moby/sys/userns v0.1.0 // indirect @@ -318,16 +320,17 @@ require ( github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/opencontainers/runtime-spec v1.2.0 // indirect - github.com/opencontainers/selinux v1.11.0 // indirect + github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 // indirect + github.com/opencontainers/selinux v1.11.1 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/prometheus/client_golang v1.20.2 // indirect + github.com/prometheus/client_golang v1.20.5 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect @@ -353,6 +356,7 @@ require ( github.com/spf13/afero v1.11.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect + github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tchap/go-patricia/v2 v2.3.1 // indirect github.com/theupdateframework/go-tuf v0.7.0 // indirect @@ -375,25 +379,25 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.mongodb.org/mongo-driver v1.14.0 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect - go.opentelemetry.io/otel v1.28.0 // indirect - go.opentelemetry.io/otel/metric v1.28.0 // indirect - go.opentelemetry.io/otel/sdk v1.28.0 // indirect - go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect + go.opentelemetry.io/otel v1.31.0 // indirect + go.opentelemetry.io/otel/metric v1.31.0 // indirect + go.opentelemetry.io/otel/sdk v1.31.0 // indirect + go.opentelemetry.io/otel/trace v1.31.0 // indirect go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/oauth2 v0.22.0 // indirect golang.org/x/sys v0.26.0 // indirect golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7 // indirect golang.org/x/time v0.6.0 // indirect golang.org/x/tools v0.24.0 // indirect google.golang.org/api v0.172.0 // indirect google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect - google.golang.org/grpc v1.66.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect + google.golang.org/grpc v1.67.1 // indirect gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/go-jose/go-jose.v2 v2.6.3 // indirect @@ -403,7 +407,7 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/apiextensions-apiserver v0.31.0 // indirect k8s.io/apimachinery v0.31.2 // indirect - k8s.io/apiserver v0.31.0 // indirect + k8s.io/apiserver v0.31.2 // indirect k8s.io/cli-runtime v0.31.2 // indirect k8s.io/client-go v0.31.2 // indirect k8s.io/component-base v0.31.2 // indirect @@ -422,4 +426,6 @@ require ( sigs.k8s.io/kustomize/api v0.17.2 // indirect sigs.k8s.io/kustomize/kyaml v0.17.1 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + tags.cncf.io/container-device-interface v0.8.0 // indirect + tags.cncf.io/container-device-interface/specs-go v0.8.0 // indirect ) diff --git a/go.sum b/go.sum index f2112b2721ef..7618669c665c 100644 --- a/go.sum +++ b/go.sum @@ -68,8 +68,8 @@ cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= -cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= @@ -193,10 +193,10 @@ dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= -github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 h1:59MxjQVfjXsBpLy+dbd2/ELV5ofnUkUZBvWSC85sheA= -github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0/go.mod h1:OahwfttHWG6eJ0clwcfBAHoDI6X/LV/15hx/wlMZSrU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2 h1:dIScnXFlF784X79oi7MzVT6GWqr/W1uUt0pB5CsDs9M= +github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20231105174938-2b5cbb29f3e2/go.mod h1:gCLVsLfv1egrcZu+GoJATN5ts75F2s62ih/457eWzOw= github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d h1:zjqpY4C7H15HjRPEenkS4SAn3Jy2eRRjkjZbGR30TOg= github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d/go.mod h1:XNqJ7hv2kY++g8XEHREpi+JqZo3+0l+CH2egBVN4yqM= github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0 h1:8+4G8JaejP8Xa6W46PzJEwisNgBXMvFcz78N6zG/ARw= @@ -266,8 +266,8 @@ github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA4 github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/Microsoft/hcsshim v0.12.0 h1:rbICA+XZFwrBef2Odk++0LjFvClNCJGRK+fsrP254Ts= -github.com/Microsoft/hcsshim v0.12.0/go.mod h1:RZV12pcHCXQ42XnlQ3pz6FZfmrC1C+R4gaOHhRNML1g= +github.com/Microsoft/hcsshim v0.12.9 h1:2zJy5KA+l0loz1HzEGqyNnjd3fyZA31ZBCGKacp6lLg= +github.com/Microsoft/hcsshim v0.12.9/go.mod h1:fJ0gkFAna6ukt0bLdKB8djt4XIJhF/vEPuoIWYVvZ8Y= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= @@ -480,36 +480,43 @@ github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUo github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ= github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w= -github.com/containerd/cgroups/v3 v3.0.2 h1:f5WFqIVSgo5IZmtTT3qVBo6TzI1ON6sycSBKkymb9L0= -github.com/containerd/cgroups/v3 v3.0.2/go.mod h1:JUgITrzdFqp42uI2ryGA+ge0ap/nxzYgkGmIcetmErE= -github.com/containerd/containerd v1.7.22 h1:nZuNnNRA6T6jB975rx2RRNqqH2k6ELYKDZfqTHqwyy0= -github.com/containerd/containerd v1.7.22/go.mod h1:e3Jz1rYRUZ2Lt51YrH9Rz0zPyJBOlSvB3ghr2jbVD8g= -github.com/containerd/containerd/api v1.7.19 h1:VWbJL+8Ap4Ju2mx9c9qS1uFSB1OVYr5JJrW2yT5vFoA= -github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig= -github.com/containerd/continuity v0.4.3 h1:6HVkalIp+2u1ZLH1J/pYX2oBVXlJZvh1X1A7bEZ9Su8= -github.com/containerd/continuity v0.4.3/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= -github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM= -github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0= +github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0= +github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0= +github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ= +github.com/containerd/containerd v1.7.23/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw= +github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0= +github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc= +github.com/containerd/containerd/v2 v2.0.0 h1:qLDdFaAykQrIyLiqwQrNLLz95wiC36bAZVwioUwqShM= +github.com/containerd/containerd/v2 v2.0.0/go.mod h1:j25kDy9P48/ngb1sxWIFfK6GsnqOHoSqo1EpAod20VQ= +github.com/containerd/continuity v0.4.4 h1:/fNVfTJ7wIl/YPMHjf+5H32uFhl63JucB34PlCpMKII= +github.com/containerd/continuity v0.4.4/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY= github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= -github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/containerd/platforms v1.0.0-rc.0 h1:GuHWSKgVVO3POn6nRBB4sH63uPOLa87yuuhsGLWaXAA= +github.com/containerd/platforms v1.0.0-rc.0/go.mod h1:T1XAzzOdYs3it7l073MNXyxRwQofJfqwi/8cRjufIk4= +github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y= +github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8= github.com/containerd/stargz-snapshotter/estargz v0.15.1 h1:eXJjw9RbkLFgioVaTG+G/ZW/0kEe2oEKCdS/ZxIyoCU= github.com/containerd/stargz-snapshotter/estargz v0.15.1/go.mod h1:gr2RNwukQ/S9Nv33Lt6UC7xEx58C+LHRdoqbEKjz1Kk= -github.com/containerd/ttrpc v1.2.5 h1:IFckT1EFQoFBMG4c3sMdT8EP3/aKfumK1msY+Ze4oLU= -github.com/containerd/ttrpc v1.2.5/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= -github.com/containerd/typeurl/v2 v2.2.0 h1:6NBDbQzr7I5LHgp34xAXYF5DOTQDn05X58lsPEmzLso= -github.com/containerd/typeurl/v2 v2.2.0/go.mod h1:8XOOxnyatxSWuG8OfsZXVnAF4iZfedjS/8UHSPJnX4g= +github.com/containerd/ttrpc v1.2.6 h1:zG+Kn5EZ6MUYCS1t2Hmt2J4tMVaLSFEJVOraDQwNPC4= +github.com/containerd/ttrpc v1.2.6/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= +github.com/containerd/typeurl/v2 v2.2.2 h1:3jN/k2ysKuPCsln5Qv8bzR9cxal8XjkxPogJfSNO31k= +github.com/containerd/typeurl/v2 v2.2.2/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk= github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= github.com/coreos/go-oidc/v3 v3.10.0 h1:tDnXHnLyiTVyT/2zLDGj09pFPkhND8Gl8lnTRhoEaJU= github.com/coreos/go-oidc/v3 v3.10.0/go.mod h1:5j11xcw0D3+SGxn6Z/WFADsgcWVMyNAlSQupk0KK3ac= github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= +github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0= github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/csaf-poc/csaf_distribution/v3 v3.0.0 h1:ob9+Fmpff0YWgTP3dYaw7G2hKQ9cegh9l3zksc+q3sM= @@ -638,8 +645,8 @@ github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= -github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U= -github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY= +github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E= +github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= @@ -716,8 +723,8 @@ github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= -github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY= +github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -866,8 +873,8 @@ github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJr github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -956,8 +963,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f h1:GvCU5GXhHq+7LeOzx/haG7HSIZokl3/0GkoUFzsRJjg= github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f/go.mod h1:q59u9px8b7UTj0nIjEjvmTWekazka6xIt6Uogz5Dm+8= @@ -1064,6 +1071,7 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs= github.com/moby/buildkit v0.16.0 h1:wOVBj1o5YNVad/txPQNXUXdelm7Hs/i0PUFjzbK0VKE= github.com/moby/buildkit v0.16.0/go.mod h1:Xqx/5GlrqE1yIRORk0NSCVDFpQAU1WjlT6KHYZdisIQ= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= @@ -1076,8 +1084,8 @@ github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8 github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= -github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= -github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0= github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8= github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= @@ -1099,6 +1107,7 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mozillazg/docker-credential-acr-helper v0.3.0 h1:DVWFZ3/O8BP6Ue3iS/Olw+G07u1hCq1EOVCDZZjCIBI= github.com/mozillazg/docker-credential-acr-helper v0.3.0/go.mod h1:cZlu3tof523ujmLuiNUb6JsjtHcNA70u1jitrrdnuyA= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -1135,10 +1144,14 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/opencontainers/runtime-spec v1.0.3-0.20220825212826-86290f6a00fb/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk= github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= -github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= +github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 h1:DmNGcqH3WDbV5k8OJ+esPWbqUOX5rMLR2PMvziDMJi0= +github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626/go.mod h1:BRHJJd0E+cx42OybVYSgUvZmU0B8P9gZuRXlZUP7TKI= +github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/opencontainers/selinux v1.11.1 h1:nHFvthhM0qY8/m+vfhJylliSshm8G1jJ2jDMcgULaH8= +github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openvex/discovery v0.1.1-0.20240802171711-7c54efc57553 h1:c4u0GIH0w2Q57Pm2Oldrq6EiHFnLCCnRs98A+ggj/YQ= @@ -1154,8 +1167,8 @@ github.com/package-url/packageurl-go v0.1.3 h1:4juMED3hHiz0set3Vq3KeQ75KD1avthoX github.com/package-url/packageurl-go v0.1.3/go.mod h1:nKAWB8E6uk1MHqiS/lQb9pYBGH2+mdJ2PJc2s50dQY0= github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= @@ -1178,8 +1191,8 @@ github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjz github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg= -github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -1269,6 +1282,7 @@ github.com/sigstore/timestamp-authority v1.2.2 h1:X4qyutnCQqJ0apMewFyx+3t7Tws00J github.com/sigstore/timestamp-authority v1.2.2/go.mod h1:nEah4Eq4wpliDjlY342rXclGSO7Kb9hoRrl9tqLW13A= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= @@ -1317,6 +1331,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= @@ -1353,6 +1369,7 @@ github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4A github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/urfave/cli v1.19.1/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts= github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk= @@ -1422,24 +1439,24 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= -go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= -go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 h1:R3X6ZXmNPRR8ul6i3WgFURCHzaXjHdm0karRG/+dj3s= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0/go.mod h1:QWFXnDavXWwMx2EEcZsf3yxgEKAqsxQ+Syjp+seyInw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 h1:digkEZCJWobwBqMwC0cwCq8/wkkRy/OowZg5OArWZrM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0/go.mod h1:/OpE/y70qVkndM0TrxT4KBoN3RsFZP0QaofcfYrj76I= -go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= -go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= -go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= -go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= -go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 h1:yMkBS9yViCc7U7yeLzJPM2XizlfdVvBRSmsQDWu6qc0= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0/go.mod h1:n8MR6/liuGB5EmTETUBeU5ZgqMOlqKRxUaqPQBOANZ8= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM= +go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= +go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 h1:FFeLy03iVTXP6ffeN2iXrxfGsZGCjVx0/4KlizjyBwU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0/go.mod h1:TMu73/k1CP8nBUpDLc71Wj/Kf7ZS9FK5b53VapRsP9o= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4= +go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= +go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= +go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= +go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= +go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= +go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= @@ -1466,8 +1483,8 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1478,8 +1495,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= -golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 h1:qCEDpW1G+vcj3Y7Fy52pEM1AWm3abj8WimGYejI3SC4= +golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1569,8 +1586,8 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1596,8 +1613,8 @@ golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1638,6 +1655,7 @@ golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1734,8 +1752,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1973,10 +1991,10 @@ google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 h1:ImUcDPHjTrAqNhlOkSocDLfG9rrNHH7w7uoKWPaWZ8s= google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7/go.mod h1:/3XmxOjePkvmKrHuBy4zNFw7IzxJXtAgdpXi8Ll990U= -google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0= -google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg= +google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -2012,8 +2030,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c= -google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= +google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= +google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -2030,8 +2048,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -2085,8 +2103,8 @@ k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24 k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw= k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/apiserver v0.31.0 h1:p+2dgJjy+bk+B1Csz+mc2wl5gHwvNkC9QJV+w55LVrY= -k8s.io/apiserver v0.31.0/go.mod h1:KI9ox5Yu902iBnnyMmy7ajonhKnkeZYJhTZ/YI+WEMk= +k8s.io/apiserver v0.31.2 h1:VUzOEUGRCDi6kX1OyQ801m4A7AUPglpsmGvdsekmcI4= +k8s.io/apiserver v0.31.2/go.mod h1:o3nKZR7lPlJqkU5I3Ove+Zx3JuoFjQobGX1Gctw6XuE= k8s.io/cli-runtime v0.31.2 h1:7FQt4C4Xnqx8V1GJqymInK0FFsoC+fAZtbLqgXYVOLQ= k8s.io/cli-runtime v0.31.2/go.mod h1:XROyicf+G7rQ6FQJMbeDV9jqxzkWXTYD6Uxd15noe0Q= k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc= @@ -2148,3 +2166,7 @@ sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= +tags.cncf.io/container-device-interface v0.8.0 h1:8bCFo/g9WODjWx3m6EYl3GfUG31eKJbaggyBDxEldRc= +tags.cncf.io/container-device-interface v0.8.0/go.mod h1:Apb7N4VdILW0EVdEMRYXIDVRZfNJZ+kmEUss2kRRQ6Y= +tags.cncf.io/container-device-interface/specs-go v0.8.0 h1:QYGFzGxvYK/ZLMrjhvY0RjpUavIn4KcmRmVP/JjdBTA= +tags.cncf.io/container-device-interface/specs-go v0.8.0/go.mod h1:BhJIkjjPh4qpys+qm4DAYtUyryaTDg9zris+AczXyws= diff --git a/pkg/fanal/image/daemon/containerd.go b/pkg/fanal/image/daemon/containerd.go index 109081d5bae5..faa5c8d98c3f 100644 --- a/pkg/fanal/image/daemon/containerd.go +++ b/pkg/fanal/image/daemon/containerd.go @@ -10,12 +10,12 @@ import ( "strings" "time" - "github.com/containerd/containerd" - "github.com/containerd/containerd/content" - "github.com/containerd/containerd/images/archive" - "github.com/containerd/containerd/namespaces" - "github.com/containerd/containerd/platforms" - refdocker "github.com/containerd/containerd/reference/docker" + "github.com/containerd/containerd/v2/client" + "github.com/containerd/containerd/v2/core/content" + "github.com/containerd/containerd/v2/core/images/archive" + "github.com/containerd/containerd/v2/pkg/namespaces" + "github.com/containerd/platforms" + "github.com/distribution/reference" api "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/go-connections/nat" @@ -52,12 +52,12 @@ func (n familiarNamed) String() string { return string(n) } -func imageWriter(client *containerd.Client, img containerd.Image, platform types.Platform) imageSave { +func imageWriter(c *client.Client, img client.Image, platform types.Platform) imageSave { return func(ctx context.Context, ref []string) (io.ReadCloser, error) { if len(ref) < 1 { return nil, xerrors.New("no image reference") } - imgOpts := archive.WithImage(client.ImageService(), ref[0]) + imgOpts := archive.WithImage(c.ImageService(), ref[0]) manifestOpts := archive.WithManifest(img.Target()) var platformMatchComparer platforms.MatchComparer @@ -69,7 +69,7 @@ func imageWriter(client *containerd.Client, img containerd.Image, platform types platOpts := archive.WithPlatform(platformMatchComparer) pr, pw := io.Pipe() go func() { - pw.CloseWithError(archive.Export(ctx, client.ContentStore(), pw, imgOpts, manifestOpts, platOpts)) + pw.CloseWithError(archive.Export(ctx, c.ContentStore(), pw, imgOpts, manifestOpts, platOpts)) }() return pr, nil } @@ -94,17 +94,17 @@ func ContainerdImage(ctx context.Context, imageName string, opts types.ImageOpti return nil, cleanup, err } - var options []containerd.ClientOpt + var options []client.Opt if opts.RegistryOptions.Platform.Platform != nil { ociPlatform, err := platforms.Parse(opts.RegistryOptions.Platform.String()) if err != nil { return nil, cleanup, err } - options = append(options, containerd.WithDefaultPlatform(platforms.OnlyStrict(ociPlatform))) + options = append(options, client.WithDefaultPlatform(platforms.OnlyStrict(ociPlatform))) } - client, err := containerd.New(addr, options...) + c, err := client.New(addr, options...) if err != nil { return nil, cleanup, xerrors.Errorf("failed to initialize a containerd client: %w", err) } @@ -116,7 +116,7 @@ func ContainerdImage(ctx context.Context, imageName string, opts types.ImageOpti ctx = namespaces.WithNamespace(ctx, namespace) - imgs, err := client.ListImages(ctx, searchFilters...) + imgs, err := c.ListImages(ctx, searchFilters...) if err != nil { return nil, cleanup, xerrors.Errorf("failed to list images from containerd client: %w", err) } @@ -133,7 +133,7 @@ func ContainerdImage(ctx context.Context, imageName string, opts types.ImageOpti } cleanup = func() { - _ = client.Close() + _ = c.Close() _ = f.Close() _ = os.Remove(f.Name()) } @@ -144,21 +144,21 @@ func ContainerdImage(ctx context.Context, imageName string, opts types.ImageOpti } return &image{ - opener: imageOpener(ctx, ref.String(), f, imageWriter(client, img, opts.RegistryOptions.Platform)), + opener: imageOpener(ctx, ref.String(), f, imageWriter(c, img, opts.RegistryOptions.Platform)), inspect: insp, history: history, }, cleanup, nil } -func parseReference(imageName string) (refdocker.Reference, []string, error) { - ref, err := refdocker.ParseAnyReference(imageName) +func parseReference(imageName string) (reference.Reference, []string, error) { + ref, err := reference.ParseAnyReference(imageName) if err != nil { return nil, nil, xerrors.Errorf("parse error: %w", err) } - d, isDigested := ref.(refdocker.Digested) - n, isNamed := ref.(refdocker.Named) - nt, isNamedAndTagged := ref.(refdocker.NamedTagged) + d, isDigested := ref.(reference.Digested) + n, isNamed := ref.(reference.Named) + nt, isNamedAndTagged := ref.(reference.NamedTagged) // a name plus a digest // example: name@sha256:41adb3ef... @@ -168,7 +168,7 @@ func parseReference(imageName string) (refdocker.Reference, []string, error) { // comma-separated filter is logically anded return ref, []string{ fmt.Sprintf(`name~="^%s(:|@).*",target.digest==%q`, n.Name(), dgst), - fmt.Sprintf(`name~="^%s(:|@).*",target.digest==%q`, refdocker.FamiliarName(n), dgst), + fmt.Sprintf(`name~="^%s(:|@).*",target.digest==%q`, reference.FamiliarName(n), dgst), }, nil } @@ -184,7 +184,7 @@ func parseReference(imageName string) (refdocker.Reference, []string, error) { tag := nt.Tag() return familiarNamed(imageName), []string{ fmt.Sprintf(`name=="%s:%s"`, nt.Name(), tag), - fmt.Sprintf(`name=="%s:%s"`, refdocker.FamiliarName(nt), tag), + fmt.Sprintf(`name=="%s:%s"`, reference.FamiliarName(nt), tag), }, nil } @@ -193,7 +193,7 @@ func parseReference(imageName string) (refdocker.Reference, []string, error) { // readImageConfig reads the config spec (`application/vnd.oci.image.config.v1+json`) for img.platform from content store. // ported from https://github.com/containerd/nerdctl/blob/7dfbaa2122628921febeb097e7a8a86074dc931d/pkg/imgutil/imgutil.go#L377-L393 -func readImageConfig(ctx context.Context, img containerd.Image) (ocispec.Image, ocispec.Descriptor, error) { +func readImageConfig(ctx context.Context, img client.Image) (ocispec.Image, ocispec.Descriptor, error) { var config ocispec.Image configDesc, err := img.Config(ctx) // aware of img.platform @@ -211,19 +211,19 @@ func readImageConfig(ctx context.Context, img containerd.Image) (ocispec.Image, } // ported from https://github.com/containerd/nerdctl/blob/d110fea18018f13c3f798fa6565e482f3ff03591/pkg/inspecttypes/dockercompat/dockercompat.go#L279-L321 -func inspect(ctx context.Context, img containerd.Image, ref refdocker.Reference) (api.ImageInspect, []v1.History, refdocker.Reference, error) { - if _, ok := ref.(refdocker.Digested); ok { +func inspect(ctx context.Context, img client.Image, ref reference.Reference) (api.ImageInspect, []v1.History, reference.Reference, error) { + if _, ok := ref.(reference.Digested); ok { ref = familiarNamed(img.Name()) } var tag string - if tagged, ok := ref.(refdocker.Tagged); ok { + if tagged, ok := ref.(reference.Tagged); ok { tag = tagged.Tag() } var repository string - if n, isNamed := ref.(refdocker.Named); isNamed { - repository = refdocker.FamiliarName(n) + if n, isNamed := ref.(reference.Named); isNamed { + repository = reference.FamiliarName(n) } imgConfig, imgConfigDesc, err := readImageConfig(ctx, img) diff --git a/pkg/fanal/test/integration/containerd_test.go b/pkg/fanal/test/integration/containerd_test.go index 420949cd42d3..b635c3913d9c 100644 --- a/pkg/fanal/test/integration/containerd_test.go +++ b/pkg/fanal/test/integration/containerd_test.go @@ -15,13 +15,12 @@ import ( "testing" "time" - "github.com/samber/lo" - - "github.com/containerd/containerd" - "github.com/containerd/containerd/images" - "github.com/containerd/containerd/namespaces" + "github.com/containerd/containerd/v2/client" + "github.com/containerd/containerd/v2/core/images" + "github.com/containerd/containerd/v2/pkg/namespaces" dockercontainer "github.com/docker/docker/api/types/container" v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/testcontainers/testcontainers-go" @@ -37,7 +36,7 @@ import ( "github.com/aquasecurity/trivy/pkg/fanal/types" ) -func setupContainerd(t *testing.T, ctx context.Context, namespace string) *containerd.Client { +func setupContainerd(t *testing.T, ctx context.Context, namespace string) *client.Client { t.Helper() tmpDir := t.TempDir() @@ -54,9 +53,9 @@ func setupContainerd(t *testing.T, ctx context.Context, namespace string) *conta startContainerd(t, ctx, tmpDir) // Retry up to 3 times until containerd is ready - var client *containerd.Client + var c *client.Client iteration, _, err := lo.AttemptWhileWithDelay(3, 1*time.Second, func(int, time.Duration) (error, bool) { - client, err = containerd.New(socketPath) + c, err = client.New(socketPath) if err != nil { if !errors.Is(err, os.ErrPermission) { return err, false // unexpected error @@ -64,13 +63,13 @@ func setupContainerd(t *testing.T, ctx context.Context, namespace string) *conta return err, true } t.Cleanup(func() { - require.NoError(t, client.Close()) + require.NoError(t, c.Close()) }) return nil, false }) require.NoErrorf(t, err, "attempted %d times ", iteration) - return client + return c } func startContainerd(t *testing.T, ctx context.Context, hostPath string) { From 601846134cb820c3e78324a44c7f0332d01b9fc6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 13:25:40 +0400 Subject: [PATCH 077/108] chore(deps): bump the common group across 1 directory with 20 updates (#7876) Signed-off-by: dependabot[bot] Signed-off-by: knqyf263 Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: knqyf263 --- go.mod | 71 ++++---- go.sum | 154 ++++++++++-------- integration/client_server_test.go | 2 +- integration/registry_test.go | 4 +- pkg/fanal/test/integration/containerd_test.go | 5 +- pkg/fanal/test/integration/registry_test.go | 2 +- 6 files changed, 123 insertions(+), 115 deletions(-) diff --git a/go.mod b/go.mod index 566d5069c0b9..c981c884a350 100644 --- a/go.mod +++ b/go.mod @@ -6,8 +6,8 @@ toolchain go1.22.4 require ( github.com/Azure/azure-sdk-for-go v68.0.0+incompatible - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 github.com/BurntSushi/toml v1.4.0 github.com/CycloneDX/cyclonedx-go v0.9.1 github.com/GoogleCloudPlatform/docker-credential-gcr v2.0.5+incompatible @@ -15,7 +15,7 @@ require ( github.com/NYTimes/gziphandler v1.1.1 github.com/alecthomas/chroma v0.10.0 github.com/alicebob/miniredis/v2 v2.33.0 - github.com/antchfx/htmlquery v1.3.2 + github.com/antchfx/htmlquery v1.3.3 github.com/apparentlymart/go-cidr v1.1.0 github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce @@ -29,26 +29,26 @@ require ( github.com/aquasecurity/trivy-db v0.0.0-20240910133327-7e0f4d2ed4c1 github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20241101182546-89bffc3932bc - github.com/aws/aws-sdk-go-v2 v1.31.0 - github.com/aws/aws-sdk-go-v2/config v1.27.38 - github.com/aws/aws-sdk-go-v2/credentials v1.17.36 - github.com/aws/aws-sdk-go-v2/service/ec2 v1.179.1 - github.com/aws/aws-sdk-go-v2/service/ecr v1.35.2 - github.com/aws/aws-sdk-go-v2/service/s3 v1.63.2 - github.com/aws/aws-sdk-go-v2/service/sts v1.31.2 // indirect - github.com/aws/smithy-go v1.21.0 + github.com/aws/aws-sdk-go-v2 v1.32.3 + github.com/aws/aws-sdk-go-v2/config v1.28.1 + github.com/aws/aws-sdk-go-v2/credentials v1.17.42 + github.com/aws/aws-sdk-go-v2/service/ec2 v1.187.0 + github.com/aws/aws-sdk-go-v2/service/ecr v1.36.3 + github.com/aws/aws-sdk-go-v2/service/s3 v1.66.2 + github.com/aws/aws-sdk-go-v2/service/sts v1.32.3 // indirect + github.com/aws/smithy-go v1.22.0 github.com/bitnami/go-version v0.0.0-20231130084017-bb00604d650c - github.com/bmatcuk/doublestar/v4 v4.6.1 + github.com/bmatcuk/doublestar/v4 v4.7.1 github.com/cenkalti/backoff/v4 v4.3.0 github.com/cheggaaa/pb/v3 v3.1.5 github.com/containerd/containerd/v2 v2.0.0 github.com/containerd/platforms v1.0.0-rc.0 github.com/csaf-poc/csaf_distribution/v3 v3.0.0 github.com/distribution/reference v0.6.0 - github.com/docker/cli v27.2.1+incompatible + github.com/docker/cli v27.3.1+incompatible github.com/docker/docker v27.3.1+incompatible github.com/docker/go-connections v0.5.0 - github.com/fatih/color v1.17.0 + github.com/fatih/color v1.18.0 github.com/go-git/go-git/v5 v5.12.0 github.com/go-openapi/runtime v0.28.0 // indirect github.com/go-openapi/strfmt v0.23.0 // indirect @@ -90,8 +90,8 @@ require ( github.com/mitchellh/go-homedir v1.1.0 github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/mitchellh/mapstructure v1.5.0 - github.com/moby/buildkit v0.16.0 - github.com/open-policy-agent/opa v0.68.1-0.20240903211041-76f7038ea2d1 + github.com/moby/buildkit v0.17.0 + github.com/open-policy-agent/opa v0.70.0 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.0 github.com/openvex/discovery v0.1.1-0.20240802171711-7c54efc57553 @@ -112,17 +112,17 @@ require ( github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 - github.com/testcontainers/testcontainers-go v0.33.0 - github.com/testcontainers/testcontainers-go/modules/localstack v0.33.0 - github.com/tetratelabs/wazero v1.8.0 + github.com/testcontainers/testcontainers-go v0.34.0 + github.com/testcontainers/testcontainers-go/modules/localstack v0.34.0 + github.com/tetratelabs/wazero v1.8.1 github.com/twitchtv/twirp v8.1.3+incompatible github.com/xeipuuv/gojsonschema v1.2.0 github.com/xlab/treeprint v1.2.0 github.com/zclconf/go-cty v1.15.0 - github.com/zclconf/go-cty-yaml v1.0.3 + github.com/zclconf/go-cty-yaml v1.1.0 go.etcd.io/bbolt v1.3.11 golang.org/x/crypto v0.28.0 - golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 // indirect + golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect golang.org/x/mod v0.21.0 golang.org/x/net v0.30.0 golang.org/x/sync v0.8.0 @@ -132,7 +132,7 @@ require ( golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 google.golang.org/protobuf v1.35.1 gopkg.in/yaml.v3 v3.0.1 - helm.sh/helm/v3 v3.16.1 + helm.sh/helm/v3 v3.16.2 k8s.io/api v0.31.2 k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 modernc.org/sqlite v1.33.1 @@ -169,22 +169,22 @@ require ( github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect github.com/VividCortex/ewma v1.2.0 // indirect github.com/agext/levenshtein v1.2.3 // indirect - github.com/agnivade/levenshtein v1.1.1 // indirect + github.com/agnivade/levenshtein v1.2.0 // indirect github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect - github.com/antchfx/xpath v1.3.1 // indirect + github.com/antchfx/xpath v1.3.2 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/aws/aws-sdk-go v1.55.5 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.18 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect github.com/aws/aws-sdk-go-v2/service/ebs v1.22.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.23.2 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.3 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.3 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/blang/semver v3.5.1+incompatible // indirect @@ -205,7 +205,7 @@ require ( github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect github.com/containerd/ttrpc v1.2.6 // indirect github.com/containerd/typeurl/v2 v2.2.2 // indirect - github.com/cpuguy83/dockercfg v0.3.1 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 // indirect github.com/cyphar/filepath-securejoin v0.3.1 // indirect @@ -328,6 +328,7 @@ require ( github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/prometheus/client_golang v1.20.5 // indirect @@ -391,8 +392,8 @@ require ( golang.org/x/oauth2 v0.22.0 // indirect golang.org/x/sys v0.26.0 // indirect golang.org/x/telemetry v0.0.0-20240522233618-39ace7a40ae7 // indirect - golang.org/x/time v0.6.0 // indirect - golang.org/x/tools v0.24.0 // indirect + golang.org/x/time v0.7.0 // indirect + golang.org/x/tools v0.25.0 // indirect google.golang.org/api v0.172.0 // indirect google.golang.org/genproto v0.0.0-20240311173647-c811ad7063a7 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect @@ -405,7 +406,7 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/apiextensions-apiserver v0.31.0 // indirect + k8s.io/apiextensions-apiserver v0.31.1 // indirect k8s.io/apimachinery v0.31.2 // indirect k8s.io/apiserver v0.31.2 // indirect k8s.io/cli-runtime v0.31.2 // indirect diff --git a/go.sum b/go.sum index 7618669c665c..8df679b57a6b 100644 --- a/go.sum +++ b/go.sum @@ -203,10 +203,12 @@ github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0/go.mod h1:GgeIE+1be8Ivm7Sh4RgwI42aTtC9qrcj+Y9Y6CjJhJs= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 h1:JZg6HRh6W6U4OLl6lk7BZ7BLisIzM9dG1R50zUk9C/M= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0/go.mod h1:YL1xnZ6QejvQHWJrX/AvhFl4WW4rqHVoKspWNVwFk0M= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0 h1:+m0M/LFxN43KvULkDNfdXOgrjtg6UYJPFBJyuEcRCAw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0/go.mod h1:PwOyop78lveYMRs6oCxjiVyBdyCgIYH6XHIVZO9/SFQ= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.1.0 h1:DRiANoJTiW6obBQe3SqZizkuV1PEgfiiGivmVocDy64= @@ -235,6 +237,8 @@ github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+Z github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= +github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -283,8 +287,8 @@ github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1o github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= -github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8= -github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= +github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY= +github.com/agnivade/levenshtein v1.2.0/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -321,10 +325,10 @@ github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1 github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= -github.com/antchfx/htmlquery v1.3.2 h1:85YdttVkR1rAY+Oiv/nKI4FCimID+NXhDn82kz3mEvs= -github.com/antchfx/htmlquery v1.3.2/go.mod h1:1mbkcEgEarAokJiWhTfr4hR06w/q2ZZjnYLrDt6CTUk= -github.com/antchfx/xpath v1.3.1 h1:PNbFuUqHwWl0xRjvUPjJ95Agbmdj2uzzIwmQKgu4oCk= -github.com/antchfx/xpath v1.3.1/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= +github.com/antchfx/htmlquery v1.3.3 h1:x6tVzrRhVNfECDaVxnZi1mEGrQg3mjE/rxbH2Pe6dNE= +github.com/antchfx/htmlquery v1.3.3/go.mod h1:WeU3N7/rL6mb6dCwtE30dURBnBieKDC/fR8t6X+cKjU= +github.com/antchfx/xpath v1.3.2 h1:LNjzlsSjinu3bQpw9hWMY9ocB80oLOWuQqFvO6xt51U= +github.com/antchfx/xpath v1.3.2/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apparentlymart/go-cidr v1.1.0 h1:2mAhrMoF+nhXqxTzSZMUzDHkLjmIHC+Zzn4tdgBZjnU= github.com/apparentlymart/go-cidr v1.1.0/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= @@ -366,44 +370,44 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:W github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.31.0 h1:3V05LbxTSItI5kUqNwhJrrrY1BAXxXt0sN0l72QmG5U= -github.com/aws/aws-sdk-go-v2 v1.31.0/go.mod h1:ztolYtaEUtdpf9Wftr31CJfLVjOnD/CVRkKOOYgF8hA= -github.com/aws/aws-sdk-go-v2/config v1.27.38 h1:mMVyJJuSUdbD4zKXoxDgWrgM60QwlFEg+JhihCq6wCw= -github.com/aws/aws-sdk-go-v2/config v1.27.38/go.mod h1:6xOiNEn58bj/64MPKx89r6G/el9JZn8pvVbquSqTKK4= -github.com/aws/aws-sdk-go-v2/credentials v1.17.36 h1:zwI5WrT+oWWfzSKoTNmSyeBKQhsFRJRv+PGW/UZW+Yk= -github.com/aws/aws-sdk-go-v2/credentials v1.17.36/go.mod h1:3AG/sY1rc9NJrNWcN/3KPU4SIDPGTrd/qegKB0TnFdE= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14 h1:C/d03NAmh8C4BZXhuRNboF/DqhBkBCeDiJDcaqIT5pA= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.14/go.mod h1:7I0Ju7p9mCIdlrfS+JCgqcYD0VXz/N4yozsox+0o078= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18 h1:kYQ3H1u0ANr9KEKlGs/jTLrBFPo8P8NaH/w7A01NeeM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.18/go.mod h1:r506HmK5JDUh9+Mw4CfGJGSSoqIiLCndAuqXuhbv67Y= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18 h1:Z7IdFUONvTcvS7YuhtVxN99v2cCoHRXOS4mTr0B/pUc= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.18/go.mod h1:DkKMmksZVVyat+Y+r1dEOgJEfUeA7UngIHWeKsi0yNc= +github.com/aws/aws-sdk-go-v2 v1.32.3 h1:T0dRlFBKcdaUPGNtkBSwHZxrtis8CQU17UpNBZYd0wk= +github.com/aws/aws-sdk-go-v2 v1.32.3/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo= +github.com/aws/aws-sdk-go-v2/config v1.28.1 h1:oxIvOUXy8x0U3fR//0eq+RdCKimWI900+SV+10xsCBw= +github.com/aws/aws-sdk-go-v2/config v1.28.1/go.mod h1:bRQcttQJiARbd5JZxw6wG0yIK3eLeSCPdg6uqmmlIiI= +github.com/aws/aws-sdk-go-v2/credentials v1.17.42 h1:sBP0RPjBU4neGpIYyx8mkU2QqLPl5u9cmdTWVzIpHkM= +github.com/aws/aws-sdk-go-v2/credentials v1.17.42/go.mod h1:FwZBfU530dJ26rv9saAbxa9Ej3eF/AK0OAY86k13n4M= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.18 h1:68jFVtt3NulEzojFesM/WVarlFpCaXLKaBxDpzkQ9OQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.18/go.mod h1:Fjnn5jQVIo6VyedMc0/EhPpfNlPl7dHV916O6B+49aE= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 h1:Jw50LwEkVjuVzE1NzkhNKkBf9cRN7MtE1F/b2cOKTUM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22/go.mod h1:Y/SmAyPcOTmpeVaWSzSKiILfXTVJwrGmYZhcRbhWuEY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 h1:981MHwBaRZM7+9QSR6XamDzF/o7ouUGxFzr+nVSIhrs= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22/go.mod h1:1RA1+aBEfn+CAB/Mh0MB6LsdCYCnjZm7tKXtnk499ZQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= github.com/aws/aws-sdk-go-v2/service/ebs v1.22.1 h1:SeDJWG4pmye+/aO6k+zt9clPTUy1MXqUmkW8rbAddQg= github.com/aws/aws-sdk-go-v2/service/ebs v1.22.1/go.mod h1:wRzaW0v9GGQS0h//wpsVDw3Hah5gs5UP+NxoyGeZIGM= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.179.1 h1:TwFjSwRn1kR1i1qeq5cQBRwRaZ80JQS8BHsJTb6QBk8= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.179.1/go.mod h1:W6sNzs5T4VpZn1Vy+FMKw8s24vt5k6zPJXcNOK0asBo= -github.com/aws/aws-sdk-go-v2/service/ecr v1.35.2 h1:bVNvja4oEB7v+VL1yP46hWthCPp+KYpZBLS2AifM5PY= -github.com/aws/aws-sdk-go-v2/service/ecr v1.35.2/go.mod h1:oRaGEExKI6Pqcow+Tt7wpJf73/Srcj/CUJv5Eb9QFhg= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.187.0 h1:cA4hWo269CN5RY7Arqt8BfzXF0KIN8DSNo/KcqHKkWk= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.187.0/go.mod h1:ossaD9Z1ugYb6sq9QIqQLEOorCGcqUoxlhud9M9yE70= +github.com/aws/aws-sdk-go-v2/service/ecr v1.36.3 h1:bqmoQEKpWFRDRxOv4lC5yZLc+N1cogZHPLeQACfVUJo= +github.com/aws/aws-sdk-go-v2/service/ecr v1.36.3/go.mod h1:KwOqlt4MOBK9EpOGkj8RU9fqfTEae5AOUHi1pDEZ3OQ= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.18.2 h1:PpbXaecV3sLAS6rjQiaKw4/jyq3Z8gNzmoJupHAoBp0= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.18.2/go.mod h1:fUHpGXr4DrXkEDpGAjClPsviWf+Bszeb0daKE0blxv8= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5 h1:QFASJGfT8wMXtuP3D5CRmMjARHv9ZmzFUMJznHDOY3w= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.5/go.mod h1:QdZ3OmoIjSX+8D1OPAzPxDfjXASbBMDsz9qvtyIhtik= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20 h1:Xbwbmk44URTiHNx6PNo0ujDE6ERlsCKJD3u1zfnzAPg= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.20/go.mod h1:oAfOFzUB14ltPZj1rWwRc3d/6OgD76R8KlvU3EqM9Fg= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 h1:TToQNkvGguu209puTojY/ozlqy2d/SFNcoLIqTFi42g= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0/go.mod h1:0jp+ltwkf+SwG2fm/PKo8t4y8pJSgOCO4D8Lz3k0aHQ= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3 h1:qcxX0JYlgWH3hpPUnd6U0ikcl6LLA9sLkXE2w1fpMvY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.3/go.mod h1:cLSNEmI45soc+Ef8K/L+8sEA3A3pYFEYf5B5UI+6bH4= github.com/aws/aws-sdk-go-v2/service/kms v1.30.0 h1:yS0JkEdV6h9JOo8sy2JSpjX+i7vsKifU8SIeHrqiDhU= github.com/aws/aws-sdk-go-v2/service/kms v1.30.0/go.mod h1:+I8VUUSVD4p5ISQtzpgSva4I8cJ4SQ4b1dcBcof7O+g= -github.com/aws/aws-sdk-go-v2/service/s3 v1.63.2 h1:1iXmXy8SJzQVMGvo40TSzBYS9ig6BSyXfRIMzLfmBfE= -github.com/aws/aws-sdk-go-v2/service/s3 v1.63.2/go.mod h1:NLTqRLe3pUNu3nTEHI6XlHLKYmc8fbHUdMxAB6+s41Q= -github.com/aws/aws-sdk-go-v2/service/sso v1.23.2 h1:yzi/y/vKlLyzOfG7pSu5ONNGRxHIgLeDrV4w2AMRCo0= -github.com/aws/aws-sdk-go-v2/service/sso v1.23.2/go.mod h1:XRlMvmad0ZNL+75C5FYdMvbbLkd6qiqz6foR1nA1PXY= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.2 h1:3gb6pYhYLjo8rB1h2Tqs61wpjRd3rQymYcVq/pp0yxI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.27.2/go.mod h1:FnvDM4sfa+isJ3kDXIzAB9GAwVSzFzSy97uZ3IsHo4E= -github.com/aws/aws-sdk-go-v2/service/sts v1.31.2 h1:O6tyji8mXmBGsHvTCB0VIhrDw19lGTUSbKIyjnw79s8= -github.com/aws/aws-sdk-go-v2/service/sts v1.31.2/go.mod h1:yMWe0F+XG0DkRZK5ODZhG7BEFYhLXi2dqGsv6tX0cgI= -github.com/aws/smithy-go v1.21.0 h1:H7L8dtDRk0P1Qm6y0ji7MCYMQObJ5R9CRpyPhRUkLYA= -github.com/aws/smithy-go v1.21.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/aws/aws-sdk-go-v2/service/s3 v1.66.2 h1:p9TNFL8bFUMd+38YIpTAXpoxyz0MxC7FlbFEH4P4E1U= +github.com/aws/aws-sdk-go-v2/service/s3 v1.66.2/go.mod h1:fNjyo0Coen9QTwQLWeV6WO2Nytwiu+cCcWaTdKCAqqE= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.3 h1:UTpsIf0loCIWEbrqdLb+0RxnTXfWh2vhw4nQmFi4nPc= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.3/go.mod h1:FZ9j3PFHHAR+w0BSEjK955w5YD2UwB/l/H0yAK3MJvI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.3 h1:2YCmIXv3tmiItw0LlYf6v7gEHebLY45kBEnPezbUKyU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.3/go.mod h1:u19stRyNPxGhj6dRm+Cdgu6N75qnbW7+QN0q0dsAk58= +github.com/aws/aws-sdk-go-v2/service/sts v1.32.3 h1:wVnQ6tigGsRqSWDEEyH6lSAJ9OyFUsSnbaUWChuSGzs= +github.com/aws/aws-sdk-go-v2/service/sts v1.32.3/go.mod h1:VZa9yTFyj4o10YGsmDO4gbQJUvvhY72fhumT8W4LqsE= +github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM= +github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8 h1:SoFYaT9UyGkR0+nogNyD/Lj+bsixB+SNuAS4ABlEs6M= github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8/go.mod h1:2JF49jcDOrLStIXN/j/K1EKRq8a8R2qRnlZA6/o/c7c= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -418,8 +422,8 @@ github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdn github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I= -github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q= +github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0= github.com/briandowns/spinner v1.23.0 h1:alDF2guRWqa/FOZZYWjlMIx2L6H0wyewPxo/CH4Pt2A= @@ -511,8 +515,8 @@ github.com/containerd/typeurl/v2 v2.2.2/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsx github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= github.com/coreos/go-oidc/v3 v3.10.0 h1:tDnXHnLyiTVyT/2zLDGj09pFPkhND8Gl8lnTRhoEaJU= github.com/coreos/go-oidc/v3 v3.10.0/go.mod h1:5j11xcw0D3+SGxn6Z/WFADsgcWVMyNAlSQupk0KK3ac= -github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= -github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= @@ -537,8 +541,8 @@ github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWa github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= -github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= +github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= +github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/digitorus/pkcs7 v0.0.0-20230713084857-e76b763bdc49/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 h1:ge14PCmCvPjpMQMIAH7uKg0lrtNSOdpYsRXlwk3QbaE= github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= @@ -552,8 +556,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/docker/cli v27.2.1+incompatible h1:U5BPtiD0viUzjGAjV1p0MGB8eVA3L3cbIrnyWmSJI70= -github.com/docker/cli v27.2.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v27.3.1+incompatible h1:qEGdFBF3Xu6SCvCYhc7CzaQTlBmqDuzxPDpigSyeKQQ= +github.com/docker/cli v27.3.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI= @@ -600,8 +604,8 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZM github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= @@ -959,6 +963,8 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs= +github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= @@ -1072,8 +1078,8 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b/go.mod h1:pzzDgJWZ34fGzaAZGFW22KVZDfyrYW+QABMrWnJBnSs= -github.com/moby/buildkit v0.16.0 h1:wOVBj1o5YNVad/txPQNXUXdelm7Hs/i0PUFjzbK0VKE= -github.com/moby/buildkit v0.16.0/go.mod h1:Xqx/5GlrqE1yIRORk0NSCVDFpQAU1WjlT6KHYZdisIQ= +github.com/moby/buildkit v0.17.0 h1:ZA/4AxwBbve1f3ZaNNJQiCBtTV62R6YweWNwq4A+sTc= +github.com/moby/buildkit v0.17.0/go.mod h1:ru8NFyDHD8HbuKaLXJIjK9nr3x6FZR+IWjtF07S+wdM= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= @@ -1138,8 +1144,8 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= -github.com/open-policy-agent/opa v0.68.1-0.20240903211041-76f7038ea2d1 h1:GQrryTKpunLNDc2NdhNL1FzfrbuNvo45s76anGdqz9k= -github.com/open-policy-agent/opa v0.68.1-0.20240903211041-76f7038ea2d1/go.mod h1:5E5SvaPwTpwt2WM177I9Z3eT7qUpmOGjk1ZdHs+TZ4w= +github.com/open-policy-agent/opa v0.70.0 h1:B3cqCN2iQAyKxK6+GI+N40uqkin+wzIrM7YA60t9x1U= +github.com/open-policy-agent/opa v0.70.0/go.mod h1:Y/nm5NY0BX0BqjBriKUiV81sCl8XOjjvqQG7dXrggtI= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -1180,6 +1186,8 @@ github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjL github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -1213,6 +1221,8 @@ github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= +github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -1339,12 +1349,12 @@ github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BG github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= github.com/terminalstatic/go-xsd-validate v0.1.5 h1:RqpJnf6HGE2CB/lZB1A8BYguk8uRtcvYAPLCF15qguo= github.com/terminalstatic/go-xsd-validate v0.1.5/go.mod h1:18lsvYFofBflqCrvo1umpABZ99+GneNTw2kEEc8UPJw= -github.com/testcontainers/testcontainers-go v0.33.0 h1:zJS9PfXYT5O0ZFXM2xxXfk4J5UMw/kRiISng037Gxdw= -github.com/testcontainers/testcontainers-go v0.33.0/go.mod h1:W80YpTa8D5C3Yy16icheD01UTDu+LmXIA2Keo+jWtT8= -github.com/testcontainers/testcontainers-go/modules/localstack v0.33.0 h1:AhbUGUjneEnMyTV5aTsPYzDiAWrba1duPtiV+Z9CKdY= -github.com/testcontainers/testcontainers-go/modules/localstack v0.33.0/go.mod h1:J5vMq1fXXiTfwcJplMClHhn+j8+MbIMv7Lic4d9E8qU= -github.com/tetratelabs/wazero v1.8.0 h1:iEKu0d4c2Pd+QSRieYbnQC9yiFlMS9D+Jr0LsRmcF4g= -github.com/tetratelabs/wazero v1.8.0/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs= +github.com/testcontainers/testcontainers-go v0.34.0 h1:5fbgF0vIN5u+nD3IWabQwRybuB4GY8G2HHgCkbMzMHo= +github.com/testcontainers/testcontainers-go v0.34.0/go.mod h1:6P/kMkQe8yqPHfPWNulFGdFHTD8HB2vLq/231xY2iPQ= +github.com/testcontainers/testcontainers-go/modules/localstack v0.34.0 h1:WkjVmea0XQyGTY10Er8fOsVjHQ77iJCmTExnx6fC3Tw= +github.com/testcontainers/testcontainers-go/modules/localstack v0.34.0/go.mod h1:rTo76O/BBeAtfazMQqLvfwBrntBBwDP7/+Z60dm3e9U= +github.com/tetratelabs/wazero v1.8.1 h1:NrcgVbWfkWvVc4UtT4LRLDf91PsOzDzefMdwhLfA550= +github.com/tetratelabs/wazero v1.8.1/go.mod h1:yAI0XTsMBhREkM/YDAK/zNou3GoiAce1P6+rp/wQhjs= github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg= github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI= @@ -1421,8 +1431,8 @@ github.com/zclconf/go-cty v1.15.0 h1:tTCRWxsexYUmtt/wVxgDClUe+uQusuI443uL6e+5sXQ github.com/zclconf/go-cty v1.15.0/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= -github.com/zclconf/go-cty-yaml v1.0.3 h1:og/eOQ7lvA/WWhHGFETVWNduJM7Rjsv2RRpx1sdFMLc= -github.com/zclconf/go-cty-yaml v1.0.3/go.mod h1:9YLUH4g7lOhVWqUbctnVlZ5KLpg7JAprQNgxSZ1Gyxs= +github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0= +github.com/zclconf/go-cty-yaml v1.1.0/go.mod h1:9YLUH4g7lOhVWqUbctnVlZ5KLpg7JAprQNgxSZ1Gyxs= github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs= github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= @@ -1495,8 +1505,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 h1:qCEDpW1G+vcj3Y7Fy52pEM1AWm3abj8WimGYejI3SC4= -golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= +golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1757,8 +1767,8 @@ golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= -golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1817,8 +1827,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= +golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= golang.org/x/vuln v1.1.3 h1:NPGnvPOTgnjBc9HTaUx+nj+EaUYxl5SJOWqaDYGaFYw= golang.org/x/vuln v1.1.3/go.mod h1:7Le6Fadm5FOqE9C926BCD0g12NWyhg7cxV4BwcPFuNY= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -2088,8 +2098,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= -helm.sh/helm/v3 v3.16.1 h1:cER6tI/8PgUAsaJaQCVBUg3VI9KN4oVaZJgY60RIc0c= -helm.sh/helm/v3 v3.16.1/go.mod h1:r+xBHHP20qJeEqtvBXMf7W35QDJnzY/eiEBzt+TfHps= +helm.sh/helm/v3 v3.16.2 h1:Y9v7ry+ubQmi+cb5zw1Llx8OKHU9Hk9NQ/+P+LGBe2o= +helm.sh/helm/v3 v3.16.2/go.mod h1:SyTXgKBjNqi2NPsHCW5dDAsHqvGIu0kdNYNH9gQaw70= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -2099,8 +2109,8 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0= k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk= -k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= -k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= +k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40= +k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ= k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw= k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= k8s.io/apiserver v0.31.2 h1:VUzOEUGRCDi6kX1OyQ801m4A7AUPglpsmGvdsekmcI4= diff --git a/integration/client_server_test.go b/integration/client_server_test.go index 4afed3fdc155..0df09c6ae051 100644 --- a/integration/client_server_test.go +++ b/integration/client_server_test.go @@ -559,7 +559,7 @@ func TestClientServerWithRedis(t *testing.T) { }) // Terminate the Redis container - require.NoError(t, redisC.Terminate(ctx)) + require.NoError(t, testcontainers.TerminateContainer(redisC)) t.Run("sad path", func(t *testing.T) { osArgs := setupClient(t, testArgs, addr, cacheDir) diff --git a/integration/registry_test.go b/integration/registry_test.go index 25ddaf5b2814..3f0469e2c396 100644 --- a/integration/registry_test.go +++ b/integration/registry_test.go @@ -132,7 +132,7 @@ func TestRegistry(t *testing.T) { // set up auth server authC, err := setupAuthServer(ctx, baseDir) require.NoError(t, err) - defer authC.Terminate(ctx) + testcontainers.CleanupContainer(t, authC) authURL, err := getURL(ctx, authC, authPort) require.NoError(t, err) @@ -140,7 +140,7 @@ func TestRegistry(t *testing.T) { // set up registry registryC, err := setupRegistry(ctx, baseDir, authURL) require.NoError(t, err) - defer registryC.Terminate(ctx) + testcontainers.CleanupContainer(t, registryC) registryURL, err := getURL(ctx, registryC, registryPort) require.NoError(t, err) diff --git a/pkg/fanal/test/integration/containerd_test.go b/pkg/fanal/test/integration/containerd_test.go index b635c3913d9c..375c5cd77c62 100644 --- a/pkg/fanal/test/integration/containerd_test.go +++ b/pkg/fanal/test/integration/containerd_test.go @@ -101,6 +101,7 @@ func startContainerd(t *testing.T, ctx context.Context, hostPath string) { Started: true, }) require.NoError(t, err) + testcontainers.CleanupContainer(t, containerdC) _, _, err = containerdC.Exec(ctx, []string{ "chmod", @@ -108,10 +109,6 @@ func startContainerd(t *testing.T, ctx context.Context, hostPath string) { "/run/containerd/containerd.sock", }) require.NoError(t, err) - - t.Cleanup(func() { - require.NoError(t, containerdC.Terminate(ctx)) - }) } // Each of these tests imports an image and tags it with the name found in the diff --git a/pkg/fanal/test/integration/registry_test.go b/pkg/fanal/test/integration/registry_test.go index 4bca93f0ccba..35a32536600e 100644 --- a/pkg/fanal/test/integration/registry_test.go +++ b/pkg/fanal/test/integration/registry_test.go @@ -73,7 +73,7 @@ func TestTLSRegistry(t *testing.T) { Started: true, }) require.NoError(t, err) - defer registryC.Terminate(ctx) + testcontainers.CleanupContainer(t, registryC) registryURL, err := getRegistryURL(ctx, registryC, registryPort) require.NoError(t, err) From ab32297e0a8220a427fa330025f8625281e02275 Mon Sep 17 00:00:00 2001 From: Pierre Guilleminot Date: Thu, 7 Nov 2024 11:22:29 +0100 Subject: [PATCH 078/108] fix(fs): add missing defered Cleanup() call to post analyzer fs (#7882) --- pkg/fanal/artifact/local/fs.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/fanal/artifact/local/fs.go b/pkg/fanal/artifact/local/fs.go index 2f5ef7fe4ecd..b6c2d46c839a 100644 --- a/pkg/fanal/artifact/local/fs.go +++ b/pkg/fanal/artifact/local/fs.go @@ -82,6 +82,7 @@ func (a Artifact) Inspect(ctx context.Context) (artifact.Reference, error) { if err != nil { return artifact.Reference{}, xerrors.Errorf("failed to prepare filesystem for post analysis: %w", err) } + defer composite.Cleanup() err = a.walker.Walk(a.rootPath, a.artifactOption.WalkerOption, func(filePath string, info os.FileInfo, opener analyzer.Opener) error { dir := a.rootPath From 99b2db3978562689cef956a71281abb84ff0ce47 Mon Sep 17 00:00:00 2001 From: Nikita Pivkin Date: Fri, 8 Nov 2024 07:21:49 +0600 Subject: [PATCH 079/108] fix(misconf): handle null properties in CloudFormation templates (#7813) Signed-off-by: nikpivkin --- .../cloudformation/parser/file_context.go | 10 ++++ .../scanners/cloudformation/parser/parser.go | 33 +++++++------ .../cloudformation/parser/parser_test.go | 49 +++++++++++++++++++ 3 files changed, 77 insertions(+), 15 deletions(-) diff --git a/pkg/iac/scanners/cloudformation/parser/file_context.go b/pkg/iac/scanners/cloudformation/parser/file_context.go index 949add1ca7c4..e1c8cfa87f40 100644 --- a/pkg/iac/scanners/cloudformation/parser/file_context.go +++ b/pkg/iac/scanners/cloudformation/parser/file_context.go @@ -1,6 +1,8 @@ package parser import ( + "github.com/samber/lo" + "github.com/aquasecurity/trivy/pkg/iac/ignore" iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" ) @@ -71,3 +73,11 @@ func (t *FileContext) missingParameterValues() []string { } return missing } + +func (t *FileContext) stripNullProperties() { + for _, resource := range t.Resources { + resource.Inner.Properties = lo.OmitBy(resource.Inner.Properties, func(k string, v *Property) bool { + return v.IsNil() + }) + } +} diff --git a/pkg/iac/scanners/cloudformation/parser/parser.go b/pkg/iac/scanners/cloudformation/parser/parser.go index 696dfcf16349..24d85ba4bbbb 100644 --- a/pkg/iac/scanners/cloudformation/parser/parser.go +++ b/pkg/iac/scanners/cloudformation/parser/parser.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "io/fs" + "path" "path/filepath" "strings" @@ -83,7 +84,7 @@ func (p *Parser) ParseFS(ctx context.Context, fsys fs.FS, dir string) (FileConte return contexts, nil } -func (p *Parser) ParseFile(ctx context.Context, fsys fs.FS, path string) (fctx *FileContext, err error) { +func (p *Parser) ParseFile(ctx context.Context, fsys fs.FS, filePath string) (fctx *FileContext, err error) { defer func() { if e := recover(); e != nil { err = fmt.Errorf("panic during parse: %s", e) @@ -105,15 +106,15 @@ func (p *Parser) ParseFile(ctx context.Context, fsys fs.FS, path string) (fctx * } sourceFmt := YamlSourceFormat - if strings.HasSuffix(strings.ToLower(path), ".json") { + if path.Ext(filePath) == ".json" { sourceFmt = JsonSourceFormat } - f, err := fsys.Open(filepath.ToSlash(path)) + f, err := fsys.Open(filePath) if err != nil { return nil, err } - defer func() { _ = f.Close() }() + defer f.Close() content, err := io.ReadAll(f) if err != nil { @@ -123,7 +124,7 @@ func (p *Parser) ParseFile(ctx context.Context, fsys fs.FS, path string) (fctx * lines := strings.Split(string(content), "\n") fctx = &FileContext{ - filepath: path, + filepath: filePath, lines: lines, SourceFormat: sourceFmt, } @@ -131,26 +132,28 @@ func (p *Parser) ParseFile(ctx context.Context, fsys fs.FS, path string) (fctx * switch sourceFmt { case YamlSourceFormat: if err := yaml.Unmarshal(content, fctx); err != nil { - return nil, NewErrInvalidContent(path, err) + return nil, NewErrInvalidContent(filePath, err) } - fctx.Ignores = ignore.Parse(string(content), path, "") + fctx.Ignores = ignore.Parse(string(content), filePath, "") case JsonSourceFormat: if err := jfather.Unmarshal(content, fctx); err != nil { - return nil, NewErrInvalidContent(path, err) + return nil, NewErrInvalidContent(filePath, err) } } + fctx.stripNullProperties() + fctx.overrideParameters(p.overridedParameters) if params := fctx.missingParameterValues(); len(params) > 0 { - p.logger.Warn("Missing parameter values", log.FilePath(path), log.String("parameters", strings.Join(params, ", "))) + p.logger.Warn("Missing parameter values", log.FilePath(filePath), log.String("parameters", strings.Join(params, ", "))) } fctx.lines = lines fctx.SourceFormat = sourceFmt - fctx.filepath = path + fctx.filepath = filePath - p.logger.Debug("Context loaded from source", log.FilePath(path)) + p.logger.Debug("Context loaded from source", log.FilePath(filePath)) // the context must be set to conditions before resources for _, c := range fctx.Conditions { @@ -158,7 +161,7 @@ func (p *Parser) ParseFile(ctx context.Context, fsys fs.FS, path string) (fctx * } for name, r := range fctx.Resources { - r.configureResource(name, fsys, path, fctx) + r.configureResource(name, fsys, filePath, fctx) } return fctx, nil @@ -190,10 +193,10 @@ func (p *Parser) parseParams() error { return nil } -func (p *Parser) parseParametersFile(path string) (Parameters, error) { - f, err := p.configsFS.Open(path) +func (p *Parser) parseParametersFile(filePath string) (Parameters, error) { + f, err := p.configsFS.Open(filePath) if err != nil { - return nil, fmt.Errorf("parameters file %q open error: %w", path, err) + return nil, fmt.Errorf("parameters file %q open error: %w", filePath, err) } var parameters Parameters diff --git a/pkg/iac/scanners/cloudformation/parser/parser_test.go b/pkg/iac/scanners/cloudformation/parser/parser_test.go index aa058c4df855..99dd62294eee 100644 --- a/pkg/iac/scanners/cloudformation/parser/parser_test.go +++ b/pkg/iac/scanners/cloudformation/parser/parser_test.go @@ -440,3 +440,52 @@ Conditions: require.NoError(t, err) require.Len(t, files, 1) } + +func Test_TemplateWithNullProperty(t *testing.T) { + src := `AWSTemplateFormatVersion: "2010-09-09" +Resources: + TestBucket: + Type: "AWS::S3::Bucket" + Properties: + BucketName:` + + fsys := testutil.CreateFS(t, map[string]string{ + "main.yaml": src, + }) + + files, err := New().ParseFS(context.TODO(), fsys, ".") + require.NoError(t, err) + require.Len(t, files, 1) + + file := files[0] + + res := file.GetResourceByLogicalID("TestBucket") + + assert.True(t, res.GetProperty("BucketName").IsNil()) +} + +func Test_TemplateWithNullNestedProperty(t *testing.T) { + src := `AWSTemplateFormatVersion: "2010-09-09" +Description: "BAD" +Resources: + TestBucket: + Type: "AWS::S3::Bucket" + Properties: + BucketName: test + PublicAccessBlockConfiguration: + BlockPublicAcls: null` + + fsys := testutil.CreateFS(t, map[string]string{ + "main.yaml": src, + }) + + files, err := New().ParseFS(context.TODO(), fsys, ".") + require.NoError(t, err) + require.Len(t, files, 1) + + file := files[0] + + res := file.GetResourceByLogicalID("TestBucket") + + assert.True(t, res.GetProperty("PublicAccessBlockConfiguration.BlockPublicAcls").IsNil()) +} From 611558e4ce61818330118684274534f26b1fda99 Mon Sep 17 00:00:00 2001 From: Alberto Donato Date: Fri, 8 Nov 2024 08:32:54 +0100 Subject: [PATCH 080/108] fix(terraform): set null value as fallback for missing variables (#7669) --- pkg/iac/scanners/terraform/parser/parser.go | 9 ++++++ .../scanners/terraform/parser/parser_test.go | 30 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/pkg/iac/scanners/terraform/parser/parser.go b/pkg/iac/scanners/terraform/parser/parser.go index 695cbee4fb25..866835d5f8a5 100644 --- a/pkg/iac/scanners/terraform/parser/parser.go +++ b/pkg/iac/scanners/terraform/parser/parser.go @@ -217,6 +217,7 @@ func (p *Parser) Load(ctx context.Context) (*evaluator, error) { "Variable values was not found in the environment or variable files. Evaluating may not work correctly.", log.String("variables", strings.Join(missingVars, ", ")), ) + setNullMissingVariableValues(inputVars, missingVars) } } @@ -268,6 +269,14 @@ func missingVariableValues(blocks terraform.Blocks, inputVars map[string]cty.Val return missing } +// Set null values for missing variables, to allow expressions using them to be +// still be possibly evaluated to a value different than null. +func setNullMissingVariableValues(inputVars map[string]cty.Value, missingVars []string) { + for _, missingVar := range missingVars { + inputVars[missingVar] = cty.NullVal(cty.DynamicPseudoType) + } +} + func (p *Parser) EvaluateAll(ctx context.Context) (terraform.Modules, cty.Value, error) { e, err := p.Load(ctx) diff --git a/pkg/iac/scanners/terraform/parser/parser_test.go b/pkg/iac/scanners/terraform/parser/parser_test.go index 540338afb57f..9c62b958a212 100644 --- a/pkg/iac/scanners/terraform/parser/parser_test.go +++ b/pkg/iac/scanners/terraform/parser/parser_test.go @@ -1801,6 +1801,36 @@ resource "test" "fileset-func" { assert.Equal(t, []string{"a.py", "path/b.py"}, attr.GetRawValue()) } +func TestExprWithMissingVar(t *testing.T) { + files := map[string]string{ + "main.tf": ` +variable "v" { + type = string +} + +resource "test" "values" { + s = "foo-${var.v}" + l1 = ["foo", var.v] + l2 = concat(["foo"], [var.v]) + d1 = {foo = var.v} + d2 = merge({"foo": "bar"}, {"baz": var.v}) +} +`, + } + + resources := parse(t, files).GetResourcesByType("test") + require.Len(t, resources, 1) + + s_attr := resources[0].GetAttribute("s") + require.NotNil(t, s_attr) + assert.Equal(t, "foo-", s_attr.GetRawValue()) + + for _, name := range []string{"l1", "l2", "d1", "d2"} { + attr := resources[0].GetAttribute(name) + require.NotNil(t, attr) + } +} + func TestVarTypeShortcut(t *testing.T) { files := map[string]string{ "main.tf": ` From bdfcc19f86e8b7da0b611fd42633086e4ebc48e4 Mon Sep 17 00:00:00 2001 From: Robiiin Date: Mon, 11 Nov 2024 12:55:55 +0800 Subject: [PATCH 081/108] chore(k8s): enhance k8s scan log (#6997) Co-authored-by: afdesk --- pkg/k8s/commands/run.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/k8s/commands/run.go b/pkg/k8s/commands/run.go index 6a20d04aee10..1650cfa1446f 100644 --- a/pkg/k8s/commands/run.go +++ b/pkg/k8s/commands/run.go @@ -86,6 +86,7 @@ func (r *runner) run(ctx context.Context, artifacts []*k8sArtifacts.Artifact) er r.flagOpts.ScanOptions.Scanners = scanners } var rpt report.Report + log.Info("Scanning K8s...", log.String("K8s", r.cluster)) rpt, err = s.Scan(ctx, artifacts) if err != nil { return xerrors.Errorf("k8s scan error: %w", err) From 19aea4b01f3ce5a3cd05d5a1091da5b0b3ba4af6 Mon Sep 17 00:00:00 2001 From: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> Date: Thu, 14 Nov 2024 23:37:42 +0600 Subject: [PATCH 082/108] fix(report): handle `git@github.com` schema for misconfigs in `sarif` report (#7898) --- pkg/report/sarif.go | 38 ++++++++++++++++++- pkg/report/sarif_private_test.go | 59 +++++++++++++++++++++++++++++ pkg/report/sarif_test.go | 64 ++++++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 pkg/report/sarif_private_test.go diff --git a/pkg/report/sarif.go b/pkg/report/sarif.go index 3bc3344611e2..48578c70cea9 100644 --- a/pkg/report/sarif.go +++ b/pkg/report/sarif.go @@ -346,8 +346,44 @@ func ToPathUri(input string, resultClass types.ResultClass) string { return clearURI(input) } +// clearURI clears URI for misconfigs func clearURI(s string) string { - return strings.ReplaceAll(strings.ReplaceAll(s, "\\", "/"), "git::https:/", "") + s = strings.ReplaceAll(s, "\\", "/") + // cf. https://developer.hashicorp.com/terraform/language/modules/sources + switch { + case strings.HasPrefix(s, "git@github.com:"): + // build GitHub url format + // e.g. `git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git?ref=v4.2.0/main.tf` -> `github.com/terraform-aws-modules/terraform-aws-s3-bucket/tree/v4.2.0/main.tf` + // cf. https://github.com/aquasecurity/trivy/issues/7897 + s = strings.ReplaceAll(s, "git@github.com:", "github.com/") + s = strings.ReplaceAll(s, ".git", "") + s = strings.ReplaceAll(s, "?ref=", "/tree/") + case strings.HasPrefix(s, "git::https:/") && !strings.HasPrefix(s, "git::https://"): + s = strings.TrimPrefix(s, "git::https:/") + s = strings.ReplaceAll(s, ".git", "") + case strings.HasPrefix(s, "git::ssh://"): + // `"`git::ssh://username@example.com/storage.git` -> `example.com/storage.git` + if _, u, ok := strings.Cut(s, "@"); ok { + s = u + } + s = strings.ReplaceAll(s, ".git", "") + case strings.HasPrefix(s, "git::"): + // `git::https://example.com/vpc.git` -> `https://example.com/vpc` + s = strings.TrimPrefix(s, "git::") + s = strings.ReplaceAll(s, ".git", "") + case strings.HasPrefix(s, "hg::"): + // `hg::http://example.com/vpc.hg` -> `http://example.com/vpc` + s = strings.TrimPrefix(s, "hg::") + s = strings.ReplaceAll(s, ".hg", "") + case strings.HasPrefix(s, "s3::"): + // `s3::https://s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/vpc.zip` -> `https://s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/vpc.zip` + s = strings.TrimPrefix(s, "s3::") + case strings.HasPrefix(s, "gcs::"): + // `gcs::https://www.googleapis.com/storage/v1/modules/foomodule.zipp` -> `https://www.googleapis.com/storage/v1/modules/foomodule.zip` + s = strings.TrimPrefix(s, "gcs::") + } + + return s } func toUri(str string) *url.URL { diff --git a/pkg/report/sarif_private_test.go b/pkg/report/sarif_private_test.go new file mode 100644 index 000000000000..b9384599f7b0 --- /dev/null +++ b/pkg/report/sarif_private_test.go @@ -0,0 +1,59 @@ +package report + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_clearURI(t *testing.T) { + test := []struct { + name string + uri string + want string + }{ + { + name: "https", + uri: "bitbucket.org/hashicorp/terraform-consul-aws", + want: "bitbucket.org/hashicorp/terraform-consul-aws", + }, + { + name: "github", + uri: "git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git?ref=v4.2.0/main.tf", + want: "github.com/terraform-aws-modules/terraform-aws-s3-bucket/tree/v4.2.0/main.tf", + }, + { + name: "git", + uri: "git::https://example.com/storage.git?ref=51d462976d84fdea54b47d80dcabbf680badcdb8", + want: "https://example.com/storage?ref=51d462976d84fdea54b47d80dcabbf680badcdb8", + }, + { + name: "git ssh", + uri: "git::ssh://username@example.com/storage.git", + want: "example.com/storage", + }, + { + name: "hg", + uri: "hg::http://example.com/vpc.hg?ref=v1.2.0", + want: "http://example.com/vpc?ref=v1.2.0", + }, + { + name: "s3", + uri: "s3::https://s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/vpc.zip", + want: "https://s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/vpc.zip", + }, + { + name: "gcs", + uri: "gcs::https://www.googleapis.com/storage/v1/modules/foomodule.zip", + want: "https://www.googleapis.com/storage/v1/modules/foomodule.zip", + }, + } + + for _, tt := range test { + t.Run(tt.name, func(t *testing.T) { + got := clearURI(tt.uri) + require.Equal(t, tt.want, got) + require.NotNil(t, toUri(got)) + }) + } +} diff --git a/pkg/report/sarif_test.go b/pkg/report/sarif_test.go index ce68fab06a8a..c3eebef5c254 100644 --- a/pkg/report/sarif_test.go +++ b/pkg/report/sarif_test.go @@ -588,6 +588,44 @@ func TestReportWriter_Sarif(t *testing.T) { }, }, }, + { + Target: "git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git?ref=v4.2.0/main.tf", + Class: types.ClassConfig, + Type: ftypes.Terraform, + Misconfigurations: []types.DetectedMisconfiguration{ + { + Type: "Terraform Security Check", + ID: "AVD-GCP-0007", + AVDID: "AVD-GCP-0007", + Title: "Service accounts should not have roles assigned with excessive privileges", + Description: "Service accounts should have a minimal set of permissions assigned in order to do their job. They should never have excessive access as if compromised, an attacker can escalate privileges and take over the entire account.", + Message: "Service account is granted a privileged role.", + Query: "data..", + Resolution: "Limit service account access to minimal required set", + Severity: "HIGH", + PrimaryURL: "https://avd.aquasec.com/misconfig/avd-gcp-0007", + References: []string{ + "https://cloud.google.com/iam/docs/understanding-roles", + "https://avd.aquasec.com/misconfig/avd-gcp-0007", + }, + Status: "Fail", + CauseMetadata: ftypes.CauseMetadata{ + StartLine: 91, + EndLine: 91, + Occurrences: []ftypes.Occurrence{ + { + Resource: "google_project_iam_member.workload_identity_sa_bindings[\"roles/storage.admin\"]", + Filename: "git@github.com:terraform-aws-modules/terraform-aws-s3-bucket.git?ref=v4.2.0/main.tf", + Location: ftypes.Location{ + StartLine: 87, + EndLine: 93, + }, + }, + }, + }, + }, + }, + }, }, }, want: &sarif.Report{ @@ -655,6 +693,32 @@ func TestReportWriter_Sarif(t *testing.T) { }, }, }, + { + RuleID: lo.ToPtr("AVD-GCP-0007"), + RuleIndex: lo.ToPtr(uint(0)), + Level: lo.ToPtr("error"), + Message: *sarif.NewTextMessage("Artifact: github.com/terraform-aws-modules/terraform-aws-s3-bucket/tree/v4.2.0/main.tf\nType: terraform\nVulnerability AVD-GCP-0007\nSeverity: HIGH\nMessage: Service account is granted a privileged role.\nLink: [AVD-GCP-0007](https://avd.aquasec.com/misconfig/avd-gcp-0007)"), + Locations: []*sarif.Location{ + { + PhysicalLocation: sarif.NewPhysicalLocation(). + WithArtifactLocation( + &sarif.ArtifactLocation{ + URI: lo.ToPtr("github.com/terraform-aws-modules/terraform-aws-s3-bucket/tree/v4.2.0/main.tf"), + URIBaseId: lo.ToPtr("ROOTPATH"), + }, + ). + WithRegion( + &sarif.Region{ + StartLine: lo.ToPtr(91), + StartColumn: lo.ToPtr(1), + EndLine: lo.ToPtr(91), + EndColumn: lo.ToPtr(1), + }, + ), + Message: sarif.NewTextMessage("github.com/terraform-aws-modules/terraform-aws-s3-bucket/tree/v4.2.0/main.tf"), + }, + }, + }, }, ColumnKind: "utf16CodeUnits", OriginalUriBaseIDs: map[string]*sarif.ArtifactLocation{ From c8add84113467d906d3e2fc05e275951ee7f77fb Mon Sep 17 00:00:00 2001 From: Matthijs Galesloot Date: Thu, 14 Nov 2024 20:09:28 +0100 Subject: [PATCH 083/108] docs: add Headlamp to the Trivy Ecosystem page (#7916) --- docs/ecosystem/ide.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/ecosystem/ide.md b/docs/ecosystem/ide.md index e179eb7883cd..fb902b2d0809 100644 --- a/docs/ecosystem/ide.md +++ b/docs/ecosystem/ide.md @@ -1,11 +1,13 @@ # IDE and developer tools Integrations ## VSCode (Official) + [Visual Studio Code](https://code.visualstudio.com/) is an open source versatile code editor and development environment. 👉 Get it at: ## JetBrains (Official) + [JetBrains](https://jetbrains.com) makes IDEs such as Goland, Pycharm, IntelliJ, Webstorm, and more. The Trivy plugin for JetBrains IDEs lets you use Trivy right from your development environment. @@ -13,6 +15,7 @@ The Trivy plugin for JetBrains IDEs lets you use Trivy right from your developme 👉 Get it at: ## Kubernetes Lens (Official) + [Kubernetes Lens](https://k8slens.dev/) is a management application for Kubernetes clusters. Trivy has an extension for Kubernetes Lens that lets you scan Kubernetes workloads and view the results in the Lens UI. @@ -20,6 +23,7 @@ Trivy has an extension for Kubernetes Lens that lets you scan Kubernetes workloa 👉 Get it at: ## Vim (Community) + [Vim](https://www.vim.org/) is a terminal based text editor. Vim plugin for Trivy to install and run Trivy. @@ -27,6 +31,7 @@ Vim plugin for Trivy to install and run Trivy. 👉 Get it at: ## Docker Desktop (Community) + [Docker Desktop](https://www.docker.com/products/docker-desktop/) is an easy way to install [Docker]() container engine on your development machine, and manage it in a GUI . Trivy Docker Desktop extension for scanning container images for vulnerabilities and generating SBOMs @@ -34,11 +39,13 @@ Trivy Docker Desktop extension for scanning container images for vulnerabilities 👉 Get it at: ## Rancher Desktop (Community) + [Rancher Desktop](https://rancherdesktop.io/) is an easy way to use containers and Kubernetes on your development machine, and manage it in a GUI. Trivy is natively integrated with Rancher, no installation is needed. More info in Rancher documentation: ## LazyTrivy (Community) + A terminal native UI for Trivy 👉 Get it at: @@ -64,3 +71,9 @@ A trivy pre-commit hook that runs a `trivy fs` in your git repo before commiting A CDK Construct Library to scan an image with trivy in CDK codes. 👉 Get it at: + +## Headlamp plugin (Community) + +[Headlamp](https://headlamp.dev/) is a user-friendly Kubernetes UI focused on extensibility. The Kubescape plugin extends Headlamp with views on Trivy reports. + +👉 Get it at: From 0d3d934f76e543741b0aeee86cb25d795bbe6eba Mon Sep 17 00:00:00 2001 From: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> Date: Mon, 18 Nov 2024 12:25:58 +0600 Subject: [PATCH 084/108] test: change branch in spdx schema link to check in integration tests (#7935) --- integration/integration_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/integration_test.go b/integration/integration_test.go index 96c8f54a67e9..21cdfe4facd9 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -41,7 +41,7 @@ import ( var update = flag.Bool("update", false, "update golden files") -const SPDXSchema = "https://raw.githubusercontent.com/spdx/spdx-spec/development/v%s/schemas/spdx-schema.json" +const SPDXSchema = "https://raw.githubusercontent.com/spdx/spdx-spec/support/v%s/schemas/spdx-schema.json" func initDB(t *testing.T) string { fixtureDir := filepath.Join("testdata", "fixtures", "db") From 94791f83431c02a566f73e4e3f00557d3e90aef5 Mon Sep 17 00:00:00 2001 From: Itay Shakury Date: Sun, 17 Nov 2024 23:34:48 -0700 Subject: [PATCH 085/108] docs: combine trivy.dev into trivy docs (#7884) Co-authored-by: simar7 <1254783+simar7@users.noreply.github.com> --- .github/workflows/mkdocs-dev.yaml | 2 +- .github/workflows/mkdocs-latest.yaml | 2 +- docs/assets/css/trivy_v1_homepage.min.css | 1 + docs/assets/css/trivy_v1_homepage.scss | 693 ++++++++++++++++++ docs/assets/images/homepage_hero_orb_03.png | Bin 0 -> 28423 bytes docs/assets/images/homepage_hero_stars_02.svg | 1 + .../images/homepage_hero_terrain_08.svg | 1 + .../images/homepage_v1_hero_person_01.png | Bin 0 -> 56629 bytes .../images/trivy_logo_horizontal_white.svg | 1 + docs/assets/javascripts/trivy_v1_homepage.js | 9 + docs/build/Dockerfile | 8 +- docs/build/requirements.in | 3 + docs/build/requirements.txt | 136 +++- docs/docs/index.md | 5 +- docs/getting-started/index.md | 74 ++ docs/getting-started/installation.md | 116 ++- .../getting-started/signature-verification.md | 143 ++-- docs/index.md | 142 +--- docs/overrides/home.html | 244 ++++++ docs/overrides/main.html | 30 +- magefiles/magefile.go | 48 +- mkdocs.yml | 27 +- 22 files changed, 1356 insertions(+), 330 deletions(-) create mode 100644 docs/assets/css/trivy_v1_homepage.min.css create mode 100644 docs/assets/css/trivy_v1_homepage.scss create mode 100644 docs/assets/images/homepage_hero_orb_03.png create mode 100644 docs/assets/images/homepage_hero_stars_02.svg create mode 100644 docs/assets/images/homepage_hero_terrain_08.svg create mode 100644 docs/assets/images/homepage_v1_hero_person_01.png create mode 100644 docs/assets/images/trivy_logo_horizontal_white.svg create mode 100644 docs/assets/javascripts/trivy_v1_homepage.js create mode 100644 docs/build/requirements.in create mode 100644 docs/getting-started/index.md create mode 100644 docs/overrides/home.html diff --git a/.github/workflows/mkdocs-dev.yaml b/.github/workflows/mkdocs-dev.yaml index f89deb9f5151..68ca817e2259 100644 --- a/.github/workflows/mkdocs-dev.yaml +++ b/.github/workflows/mkdocs-dev.yaml @@ -22,7 +22,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip setuptools wheel - pip install git+https://${GH_TOKEN}@github.com/squidfunk/mkdocs-material-insiders.git + pip install git+https://${GH_TOKEN}@github.com/squidfunk/mkdocs-material-insiders.git@9.5.44-insiders-4.53.14 pip install -r docs/build/requirements.txt env: GH_TOKEN: ${{ secrets.MKDOCS_AQUA_BOT }} diff --git a/.github/workflows/mkdocs-latest.yaml b/.github/workflows/mkdocs-latest.yaml index 0f07db482a05..e709f9d8fa92 100644 --- a/.github/workflows/mkdocs-latest.yaml +++ b/.github/workflows/mkdocs-latest.yaml @@ -24,7 +24,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip setuptools wheel - pip install git+https://${GH_TOKEN}@github.com/squidfunk/mkdocs-material-insiders.git + pip install git+https://${GH_TOKEN}@github.com/squidfunk/mkdocs-material-insiders.git@9.5.44-insiders-4.53.14 pip install -r docs/build/requirements.txt env: GH_TOKEN: ${{ secrets.MKDOCS_AQUA_BOT }} diff --git a/docs/assets/css/trivy_v1_homepage.min.css b/docs/assets/css/trivy_v1_homepage.min.css new file mode 100644 index 000000000000..0b9ef16d1976 --- /dev/null +++ b/docs/assets/css/trivy_v1_homepage.min.css @@ -0,0 +1 @@ +body{font-family:"Inter",sans-serif}.trivy_v1_homepage_wrap{position:relative;z-index:3}.trivy_v1_homepage_wrap *{transition:all .2s ease !important}.trivy_v1_homepage_wrap .container{width:100%;margin:0 auto;max-width:1440px}@media screen and (max-width: 769px),print{.trivy_v1_homepage_wrap .container{padding:0 24px;max-width:769px}}.trivy_v1_homepage_wrap .button{background-color:#ebf3fa;border:1px solid #dbdbdb;border-width:1px;color:#363636;cursor:pointer;justify-content:center;padding-bottom:calc(.5em - 1px);padding-left:1em;padding-right:1em;padding-top:calc(.5em - 1px);text-align:center;white-space:nowrap;border-radius:4px;transition:all .2s ease;font-size:16px;display:inline-block;font-weight:700}.trivy_v1_homepage_wrap .button.is-seafoam{background-color:#00ffe4;border-color:#00ffe4;color:#07242d}.trivy_v1_homepage_wrap .button.is-seafoam.is-outlined{background-color:rgba(0,0,0,0);border-color:#00ffe4;color:#00ffe4;border-width:2px}.trivy_v1_homepage_wrap .button.is-seafoam.is-outlined:hover{background-color:#00ffe4;color:#07242d}.trivy_v1_homepage_wrap .button.large_btn{font-size:22px;padding:16px 27px;margin-right:12px}@media screen and (max-width: 769px),print{.trivy_v1_homepage_wrap .button.large_btn{font-size:18px}}.trivy_v1_homepage_wrap .button.solidseafoamarrowbutton{background-color:#00ffe4;font-weight:700;border:2px solid #00ffe4;font-size:22px;padding:16px 27px;color:#07242d}.trivy_v1_homepage_wrap .button.solidseafoamarrowbutton:after{content:"";border:solid #07242d;border-width:0 2px 2px 0;display:inline-block;padding:4px;transform:rotate(-45deg);margin-left:30px;vertical-align:middle;transition:all .2s}.trivy_v1_homepage_wrap .margin-bottom-20{margin-bottom:20px}.trivy_v1_homepage_wrap .hero_wrap{background-color:#0a0b23;background-image:radial-gradient(1600px at 70% 120%, #031145 10%, #0a0b23 100%);min-height:1050px;position:relative;z-index:10}.trivy_v1_homepage_wrap .hero_wrap .homepage_background_image_wrap{position:absolute;left:0px;top:0px;width:100%;height:100%;z-index:1;pointer-events:none}.trivy_v1_homepage_wrap .hero_wrap .homepage_background_image_wrap .stars_wrap{position:absolute;left:0px;top:0px;width:100%;height:100%;z-index:1;overflow:hidden}.trivy_v1_homepage_wrap .hero_wrap .homepage_background_image_wrap .stars_wrap .stars_bg{position:absolute;width:400vw;height:400vh;top:50%;left:50%;margin-top:-200vh;margin-left:-200vw;animation:stars_ani 240s linear infinite;background-size:240px;backface-visibility:visible;background-image:url(../images/homepage_hero_stars_02.svg);background-repeat:repeat}@keyframes stars_ani{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.trivy_v1_homepage_wrap .hero_wrap .homepage_background_image_wrap .terrain_wrap{position:absolute;left:0px;bottom:0px;width:100%;height:680px;background-image:url(../images/homepage_hero_terrain_08.svg);background-repeat:no-repeat;background-position:center top;background-size:cover;z-index:2}.trivy_v1_homepage_wrap .hero_wrap .homepage_background_image_wrap .beams_wrap{position:absolute;left:0px;bottom:0px;width:100%;height:100%;z-index:3;overflow:hidden}.trivy_v1_homepage_wrap .hero_wrap .homepage_background_image_wrap .beams_wrap .beam{position:absolute;right:200px;top:270px;width:3px;height:350%;background:rgba(62,171,255,.6);box-shadow:0px 0px 55px 0px #3eabff;transform-origin:0 0;animation:beam_ani 10s infinite}.trivy_v1_homepage_wrap .hero_wrap .homepage_background_image_wrap .beams_wrap .beam.num2{animation:beam_ani 11s infinite}.trivy_v1_homepage_wrap .hero_wrap .homepage_background_image_wrap .beams_wrap .beam.num3{animation:beam_ani 12s infinite}.trivy_v1_homepage_wrap .hero_wrap .homepage_background_image_wrap .beams_wrap .beam.num4{animation:beam_ani 13s infinite}@keyframes beam_ani{0%{transform:rotate(75deg)}50%{transform:rotate(-15deg)}100%{transform:rotate(75deg)}}.trivy_v1_homepage_wrap .hero_wrap .homepage_background_image_wrap .beams_wrap .sphere{z-index:999;position:absolute;top:60px;right:50px;width:280px;height:280px;background-image:url(../images/homepage_hero_orb_03.png);background-position:center center;background-repeat:no-repeat}.trivy_v1_homepage_wrap .hero_wrap .homepage_background_image_wrap .person_wrap{position:absolute;left:0px;bottom:0px;width:100%;height:595px;background-image:url(../images/homepage_v1_hero_person_01.png);background-repeat:no-repeat;background-position:center bottom;z-index:4}.trivy_v1_homepage_wrap .hero .hero-body{padding:80px 0px}.trivy_v1_homepage_wrap .hero .hero-body .header_title_wrap .header_title_content_wrap{width:50%;position:relative;z-index:3}.trivy_v1_homepage_wrap .hero .hero-body .header_title_wrap .header_title_content_wrap .page_title{color:#fff;font-weight:700;font-size:48px;line-height:1.3}.trivy_v1_homepage_wrap .hero .hero-body .header_title_wrap .header_title_content_wrap .page_subtitle{color:#fff;font-weight:400;font-size:24px;line-height:1.3;margin-bottom:30px}@media screen and (max-width: 1216px),print{.trivy_v1_homepage_wrap .hero .hero-body .header_title_wrap .header_title_content_wrap{width:70%}}@media screen and (max-width: 769px),print{.trivy_v1_homepage_wrap .hero .hero-body .header_title_wrap .header_title_content_wrap{width:100%}.trivy_v1_homepage_wrap .hero .hero-body .header_title_wrap .header_title_content_wrap .page_title{font-size:32px}.trivy_v1_homepage_wrap .hero .hero-body .header_title_wrap .header_title_content_wrap .page_subtitle{font-size:18px}}@media screen and (min-width: 769px),print{.trivy_v1_homepage_wrap .hero .hero-body{padding:48px 24px}}.trivy_v1_homepage_wrap .homepage_community_wrap{position:relative;background-color:#0a0b23;color:#fff;z-index:5;padding-top:60px;padding-bottom:20px}.trivy_v1_homepage_wrap .homepage_community_wrap .container.wide_container{max-width:1640px;padding-left:20px;padding-right:20px;display:flex;flex-direction:row;flex-wrap:wrap}.trivy_v1_homepage_wrap .homepage_community_wrap .community_titles_column{width:33.3333%;padding-right:32px}@media screen and (max-width: 1024px),print{.trivy_v1_homepage_wrap .homepage_community_wrap .community_titles_column{width:41.6666666667%}}@media screen and (max-width: 769px),print{.trivy_v1_homepage_wrap .homepage_community_wrap .community_titles_column{width:100%}}.trivy_v1_homepage_wrap .homepage_community_wrap .community_slider_column{width:66.6666%}@media screen and (max-width: 1024px),print{.trivy_v1_homepage_wrap .homepage_community_wrap .community_slider_column{width:58.3333333333%}}@media screen and (max-width: 769px),print{.trivy_v1_homepage_wrap .homepage_community_wrap .community_slider_column{width:100%}}.trivy_v1_homepage_wrap .homepage_community_wrap .community_title{color:#00ffe4;font-size:60px;font-weight:700;margin-bottom:24px;line-height:1.2}.trivy_v1_homepage_wrap .homepage_community_wrap .community_subtitle{color:#fff;font-size:26px;margin-bottom:24px}.trivy_v1_homepage_wrap .homepage_community_wrap .community_cta_wrap .button{font-weight:700;margin-right:10px}.trivy_v1_homepage_wrap .homepage_community_wrap .community_quotes_wrap{position:relative}.trivy_v1_homepage_wrap .homepage_community_wrap .community_quotes_wrap .community_quotes{column-count:3;column-gap:20px}@media screen and (max-width: 1216px),print{.trivy_v1_homepage_wrap .homepage_community_wrap .community_quotes_wrap .community_quotes{column-count:2}}@media screen and (max-width: 769px),print{.trivy_v1_homepage_wrap .homepage_community_wrap .community_quotes_wrap .community_quotes{column-count:1}}.trivy_v1_homepage_wrap .homepage_community_wrap .community_quotes_wrap .community_quotes .quote_item_wrap{display:inline-block;margin:0px 0px 20px 0px;width:100%}.trivy_v1_homepage_wrap .homepage_community_wrap .community_quotes_wrap .community_quotes .quote_item{display:block;position:relative;color:#fff;border:1px solid rgba(0,255,228,.2);background-color:rgba(0,255,228,.05);border-radius:4px;padding:25px}.trivy_v1_homepage_wrap .homepage_community_wrap .community_quotes_wrap .community_quotes .quote_item .quote_name{font-size:16px;font-weight:600}.trivy_v1_homepage_wrap .homepage_community_wrap .community_quotes_wrap .community_quotes .quote_item .quote_twitter_handle{opacity:.6;font-size:13px}.trivy_v1_homepage_wrap .homepage_community_wrap .community_quotes_wrap .community_quotes .quote_item .quote_company{opacity:.6;font-size:13px}.trivy_v1_homepage_wrap .homepage_community_wrap .community_quotes_wrap .community_quotes .quote_item .quote_text{font-size:16px;font-weight:400;line-height:1.3}.trivy_v1_homepage_wrap .homepage_community_wrap .community_quotes_wrap .community_quotes .quote_item .quote_avatar{display:block;position:absolute;top:25px;left:25px;width:40px;height:40px;border-radius:50%;background-repeat:no-repeat;background-position:center center;background-size:cover}.trivy_v1_homepage_wrap .homepage_community_wrap .community_quotes_wrap .community_quotes .quote_item.is_tweet .quote_text{padding-top:10px}.trivy_v1_homepage_wrap .homepage_community_wrap .community_quotes_wrap .community_quotes .quote_item.is_tweet.has_avatar .quote_name,.trivy_v1_homepage_wrap .homepage_community_wrap .community_quotes_wrap .community_quotes .quote_item.is_tweet.has_avatar .quote_twitter_handle{padding-left:50px}.trivy_v1_homepage_wrap .homepage_community_wrap .community_quotes_wrap .community_quotes .quote_item.is_quote .quote_text{position:relative;padding-top:40px;padding-bottom:10px}.trivy_v1_homepage_wrap .homepage_community_wrap .community_quotes_wrap .community_quotes .quote_item.is_quote .quote_text:before{content:"";display:block;position:absolute;top:-10px;left:0px;width:56px;height:42px;background-image:url(../images/community_quote.png);background-position:center center;background-repeat:no-repeat}@media screen and (max-width: 769px),print{.trivy_v1_homepage_wrap .homepage_community_wrap .community_title{font-size:32px}.trivy_v1_homepage_wrap .homepage_community_wrap .community_subtitle{font-size:18px}}.slick-slider{position:relative;display:block;box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-touch-callout:none;-khtml-user-select:none;-ms-touch-action:pan-y;touch-action:pan-y;-webkit-tap-highlight-color:rgba(0,0,0,0)}.slick-list{position:relative;display:block;overflow:hidden;margin:0;padding:0}.slick-list:focus{outline:none}.slick-list.dragging{cursor:hand}.slick-slider .slick-track,.slick-slider .slick-list{transform:translate3d(0, 0, 0)}.slick-track{position:relative;top:0;left:0;display:block;margin-left:auto;margin-right:auto}.slick-track:before,.slick-track:after{display:table;content:""}.slick-track:after{clear:both}.slick-loading .slick-track{visibility:hidden}.slick-slide{display:none;float:left;height:100%;min-height:1px}.slick-slide:focus{outline:none}.slick-slide img{display:block}.slick-slide.slick-loading img{display:none}.slick-slide.dragging img{pointer-events:none}.slick-initialized .slick-slide{display:block}.slick-loading .slick-slide{visibility:hidden}.slick-vertical .slick-slide{display:block;height:auto;border:1px solid rgba(0,0,0,0)}.slick-arrow.slick-hidden{display:none}.slick-arrow{display:block;background-color:rgba(0,0,0,0);border:none;color:rgba(0,0,0,0);cursor:pointer;position:absolute;top:0px;height:330px;width:80px;z-index:20;outline:none}.slick-arrow:focus,.slick-arrow:active{outline:none}.slick-arrow.slick-prev{left:0px;background-image:linear-gradient(to right, #ebf3fa 0%, rgba(235, 243, 250, 0) 100%)}.slick-arrow.slick-next{right:0px;background-image:linear-gradient(to left, #ebf3fa 0%, rgba(235, 243, 250, 0) 100%)}.slick-arrow:before{content:"";display:block;position:absolute;left:0px;top:0px;width:100%;height:100%;z-index:21;background-repeat:no-repeat}.slick-arrow.slick-prev:before{background-image:url(../images/arrow_left.png);background-position:center left}.slick-arrow.slick-next:before{background-image:url(../images/arrow_right.png);background-position:center right}.slick-dotted.slick-slider{margin-bottom:0px}.slick-dots{position:relative;display:block;width:100%;padding:0;margin:0;list-style:none;text-align:center}.slick-dots li{position:relative;display:inline-block;width:24px;height:24px;margin:0px 4px;padding:0;cursor:pointer}.slick-dots li button{font-size:0;line-height:0;display:block;width:24px;height:24px;padding:0px;cursor:pointer;color:rgba(0,0,0,0);border:0;outline:none;background:rgba(0,0,0,0)}.slick-dots li button:before{position:relative;top:0px;left:0px;width:20px;height:20px;content:"";background-color:rgba(0,0,0,0);border:2px solid #00ffe4;border-radius:50%;display:block;opacity:.7}.slick-dots li button:after{position:absolute;top:7px;left:5px;width:10px;height:10px;content:"";background-color:#00ffe4;border-radius:50%;display:block;opacity:0;transition:.2s ease-out}.slick-dots li button:hover,.slick-dots li button:focus{outline:none}.slick-dots li button:hover:after,.slick-dots li button:focus:after{opacity:1}.slick-dots li.slick-active button:after{opacity:1} diff --git a/docs/assets/css/trivy_v1_homepage.scss b/docs/assets/css/trivy_v1_homepage.scss new file mode 100644 index 000000000000..7dd40ba43a11 --- /dev/null +++ b/docs/assets/css/trivy_v1_homepage.scss @@ -0,0 +1,693 @@ +/* trivy homepage */ + +//aqua brand colors +$aq-royal-blue: #1904da; +$aq-legacy-blue: #08b1d5; +$aq-coral-red: #ff445f; +$aq-starfish-yellow: #ffc900; +$aq-dark-abyss: #07242d; +$aq-deep-sea-blue: #183278; +$aq-ocean-ash: #405a75; +$aq-sea-foam: #00ffe4; + +$aq-neo-background: #ebf3fa; +$aq-neo-background-hover: #f0f8ff; + + +$aq-royal-blue-dark: #1503ba; + +$aq-trivy-dark: #0a0b23; + + +$weight-normal: 400; +$weight-semibold: 600; +$weight-bold: 700; + + + +$gap: 32px; +// 960, 1152, and 1344 have been chosen because they are divisible by both 12 and 16 +$tablet: 769px; + +// 960px container + 4rem +$desktop: 960px + 2 * $gap; + +// 1152px container + 4rem +$widescreen: 1152px + 2 * $gap; +$widescreen-enabled: true; + +// 1344px container + 4rem +$fullhd: 1344px + 2 * $gap; +$fullhd-enabled: true; + + + +body { + + font-family: "Inter", sans-serif; +} + +.trivy_v1_homepage_wrap { + position: relative; + z-index: 3; + + * { + transition: all 0.2s ease !important; + } + + .container { + width: 100%; + margin: 0 auto; + max-width: 1440px; + + @media screen and (max-width: $tablet), print { //769 + padding: 0 24px; + max-width: $tablet; //769 + } //until tablet + } + + .button { + + background-color: #ebf3fa; + border: 1px solid #dbdbdb; + border-width: 1px; + color: #363636; + cursor: pointer; + justify-content: center; + padding-bottom: calc(.5em - 1px); + padding-left: 1em; + padding-right: 1em; + padding-top: calc(.5em - 1px); + text-align: center; + white-space: nowrap; + border-radius: 4px; + transition: all .2s ease; + font-size: 16px; + display: inline-block; + font-weight: 700; + + &.is-seafoam { + background-color: $aq-sea-foam; + border-color: $aq-sea-foam; + color: $aq-dark-abyss; + + + &.is-outlined { + background-color: rgba(0,0,0,0); + border-color: $aq-sea-foam; + color: $aq-sea-foam; + border-width: 2px; + + &:hover { + background-color: $aq-sea-foam; + color: $aq-dark-abyss; + } + } //is-outlines + + } //is-seafoam + + &.large_btn { + font-size: 22px; + padding: 16px 27px; + margin-right: 12px; + + @media screen and (max-width: $tablet), print { + font-size: 18px; + } //until tablet + } + + + + &.solidseafoamarrowbutton { + + background-color: $aq-sea-foam; + font-weight: 700; + border: 2px solid $aq-sea-foam; + font-size: 22px; //1.375rem; //1.125rem; + padding: 16px 27px; + color: $aq-dark-abyss; + + + &:after { + content: ""; + border: solid $aq-dark-abyss; + border-width: 0 2px 2px 0; + display: inline-block; + padding: 4px; + transform: rotate(-45deg); + margin-left: 30px; + vertical-align: middle; + transition: all .2s; + } + } //solidseafoamarrowbutton + + } //button + + .margin-bottom-20 { + margin-bottom: 20px; + } + + .hero_wrap { + background-color: $aq-trivy-dark; + background-image: radial-gradient(1600px at 70% 120%, #031145 10%, $aq-trivy-dark 100%); + min-height: 1050px; + position: relative; + z-index: 10; + + + + + + + .homepage_background_image_wrap { + position: absolute; + left: 0px; + top: 0px; + width: 100%; + height: 100%; + z-index: 1; + pointer-events: none; + + + .stars_wrap { + position: absolute; + left: 0px; + top: 0px; + width: 100%; + height: 100%; + z-index: 1; + overflow: hidden; + + .stars_bg { + position: absolute; + width: 400vw; + height: 400vh; + top: 50%; + left: 50%; + margin-top: -200vh; + margin-left: -200vw; + animation: stars_ani 240s linear infinite; + background-size: 240px; + backface-visibility: visible; + background-image:url(../images/homepage_hero_stars_02.svg); + background-repeat: repeat; + + } + + + @keyframes stars_ani { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + + } //stars_wrap + + .terrain_wrap { + position: absolute; + left: 0px; + bottom: 0px; + width: 100%; + height: 680px; + background-image:url(../images/homepage_hero_terrain_08.svg); + background-repeat: no-repeat; + background-position: center top; + background-size: cover; + z-index: 2; + } // terrain_wrap + + + .beams_wrap { + position: absolute; + left: 0px; + bottom: 0px; + width: 100%; + height: 100%; + z-index: 3; + overflow: hidden; + + .beam { + position: absolute; + right: 200px; + top: 270px; + width: 3px; + height: 350%; + background: rgba(#3eabff,0.6); + box-shadow: 0px 0px 55px 0px rgba(#3eabff,1); + transform-origin: 0 0; + animation: beam_ani 10s infinite; + + &.num2 {animation: beam_ani 11s infinite;} + &.num3 {animation: beam_ani 12s infinite;} + &.num4 {animation: beam_ani 13s infinite;} + } //beam + + @keyframes beam_ani { + 0% { transform: rotate(75deg); } + 50% { transform: rotate(-15deg); } + 100% { transform: rotate(75deg); } + } + + .sphere { + z-index:999; + position: absolute; + top: 60px; + right: 50px; + width: 280px; + height: 280px; + background-image:url(../images/homepage_hero_orb_03.png); + background-position: center center; + background-repeat: no-repeat; + } + + } //beams_wrap + + + .person_wrap { + position: absolute; + left: 0px; + bottom: 0px; + width: 100%; + height: 595px; + background-image:url(../images/homepage_v1_hero_person_01.png); + background-repeat: no-repeat; + background-position: center bottom; + z-index: 4; + + } // person_wrap + + + + } //hero_background_image_wrap + } + + + + .hero { + + + .hero-body { + padding: 80px 0px; + // border: 1px solid red; + + .header_title_wrap { + .header_title_content_wrap { + + width: 50%; + position: relative; + z-index: 3; + + .page_title { + color: #ffffff; + font-weight: $weight-bold; + font-size: 48px; //3rem; + line-height: 1.3; + }//page_title + + .page_subtitle { + color: #ffffff; + font-weight: $weight-normal; + font-size: 24px; //1.5rem; + line-height: 1.3; + margin-bottom: 30px; + } //page_subtitle + + + @media screen and (max-width: $widescreen), print { + width: 70%; + } //until widescreen + + @media screen and (max-width: $tablet), print { //769 + + width: 100%; + + .page_title { + font-size: 32px; //2rem; + }//page_title + + .page_subtitle { + font-size: 18px; //1.125rem; + }//page_subtitle + + } //until tablet + + + } //header_title_content_wrap + + } //header_title_wrap + + @media screen and (min-width: $tablet), print { //769 + padding: 48px 24px; //3rem 1.5rem; + } + } + + } //hero + + + + + + // } //page-trivy_homepage + + + + + /* homepage_community */ + .homepage_community_wrap { + position: relative; + background-color: $aq-trivy-dark; + color: #ffffff; + z-index: 5; + padding-top: 60px; + padding-bottom: 20px; + + + .container.wide_container { + max-width: 1640px; + padding-left: 20px; + padding-right: 20px; + display: flex; + flex-direction: row; + flex-wrap: wrap; + } + + + .community_titles_column { + width: 33.3333%; + padding-right: 32px; + + @media screen and (max-width: $desktop), print { + width: 41.6666666667%; + } //until desktop + + @media screen and (max-width: $tablet), print { + width: 100%; + } //until tablet + } + + .community_slider_column { + width: 66.6666%; + + @media screen and (max-width: $desktop), print { + width: 58.3333333333%; + } //until desktop + + @media screen and (max-width: $tablet), print { + width: 100%; + } //until tablet + } + + + .community_title { + color: $aq-sea-foam; + font-size: 60px; //3.75rem; + font-weight: $weight-bold; + margin-bottom: 24px; ////1.5rem; + line-height: 1.2; + + + } + + .community_subtitle { + color: #ffffff; + font-size: 26px; //1.625rem; + margin-bottom: 24px; ////1.5rem; + + + } + + .community_cta_wrap { + + .button { + font-weight: $weight-bold; + margin-right: 10px; + } + + } + + .community_quotes_wrap { + position: relative; + + + .community_quotes { + column-count: 3; + column-gap: 20px; + + @media screen and (max-width: $widescreen), print { //1216 + column-count: 2; + } + + @media screen and (max-width: $tablet), print { //769 + column-count: 1; + } + + .quote_item_wrap { + display: inline-block; + margin: 0px 0px 20px 0px; + width: 100%; + } + + .quote_item { + + display: block; + position: relative; + color: #ffffff; + border: 1px solid rgba($aq-sea-foam,0.2); + background-color: rgba($aq-sea-foam,0.05); + border-radius: 4px; + padding: 25px; + + .quote_name { + font-size: 16px; //1rem; + font-weight: $weight-semibold; + } + + .quote_twitter_handle { + opacity: 0.6; + font-size: 13px; //0.8125rem; + } + + .quote_company { + opacity: 0.6; + font-size: 13px; //0.8125rem; + } + + .quote_text { + font-size: 16px; //1rem; + font-weight: $weight-normal; + line-height: 1.3; + } + + .quote_avatar { + display: block; + position: absolute; + top: 25px; + left: 25px; + width: 40px; + height: 40px; + border-radius: 50%; + background-repeat: no-repeat; + background-position: center center; + background-size: cover; + + } + + &.is_tweet { + + .quote_text { + padding-top: 10px; + } + + + &.has_avatar { + .quote_name, + .quote_twitter_handle { + padding-left: 50px; + } + } //has_avatar + + } //&is_tweet + + &.is_quote { + + .quote_text { + position: relative; + padding-top: 40px; + padding-bottom: 10px; + + &:before { + content: ""; + display: block; + position: absolute; + top: -10px; + left: 0px; + width: 56px; + height: 42px; + background-image: url(../images/community_quote.png); + background-position: center center; + background-repeat: no-repeat; + } + } //quote_text + + } //&is_quote + + } //quote_item + + } + + } //community_quotes_wrap + + @media screen and (max-width: $tablet), print { //tablet + + .community_title { + font-size: 32px; //2rem; + } + .community_subtitle { + font-size: 18px; //1.125rem; + } + + } //until + + + } //homepage_community_wrap + +} //trivy_homepage_wrap + + + + + +/* Slider */ +.slick-slider{position:relative;display:block;box-sizing:border-box;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-touch-callout:none;-khtml-user-select:none;-ms-touch-action:pan-y;touch-action:pan-y;-webkit-tap-highlight-color:transparent;} +.slick-list{position:relative;display:block;overflow:hidden;margin:0;padding:0;} +.slick-list:focus{outline:none;} +.slick-list.dragging{cursor:hand;} +.slick-slider .slick-track,.slick-slider .slick-list{transform:translate3d(0,0,0);} +.slick-track{position:relative;top:0;left:0;display:block;margin-left:auto;margin-right:auto;} +.slick-track:before,.slick-track:after{display:table;content:'';} +.slick-track:after{clear:both;} +.slick-loading .slick-track{visibility:hidden;} +.slick-slide{display:none;float:left;height:100%;min-height:1px;} +.slick-slide:focus{outline:none;} +.slick-slide img{display:block;} +.slick-slide.slick-loading img{display:none;} +.slick-slide.dragging img{pointer-events:none;} +.slick-initialized .slick-slide{display:block;} +.slick-loading .slick-slide{visibility:hidden;} +.slick-vertical .slick-slide{display:block;height:auto;border:1px solid transparent;} +.slick-arrow.slick-hidden{display:none;} + +.slick-arrow {display:block;background-color:transparent;border:none;color:transparent;cursor:pointer;position:absolute;top:0px;height:330px;width:80px;z-index:20;outline:none;} +.slick-arrow:focus, .slick-arrow:active {outline:none;} +.slick-arrow.slick-prev {left:0px;background-image:linear-gradient(to right, rgba($aq-neo-background,1) 0%, rgba($aq-neo-background,0) 100%);} +.slick-arrow.slick-next {right:0px;background-image:linear-gradient(to left, rgba($aq-neo-background,1) 0%, rgba($aq-neo-background,0) 100%);} +.slick-arrow:before {content:"";display:block;position:absolute;left:0px;top:0px;width:100%;height:100%;z-index:21;background-repeat:no-repeat;} +.slick-arrow.slick-prev:before {background-image:url(../images/arrow_left.png);background-position:center left;} +.slick-arrow.slick-next:before {background-image:url(../images/arrow_right.png);background-position:center right;} + + + +/* dots */ +.slick-dotted.slick-slider +{ + margin-bottom: 0px; +} + + +.slick-dots +{ + //position: absolute; + //bottom: -25px; + position: relative; + display: block; + + width: 100%; + padding: 0; + margin: 0; + + list-style: none; + + text-align: center; +} + + +.slick-dots li { + position: relative; + display: inline-block; + width: 24px; + height: 24px; + margin: 0px 4px; + padding: 0; + cursor: pointer; +} + +.slick-dots li button +{ + font-size: 0; + line-height: 0; + + display: block; + + width: 24px; + height: 24px; + padding: 0px; + + cursor: pointer; + + color: transparent; + border: 0; + outline: none; + background: transparent; + + &:before { + + position: relative; + top: 0px; + left: 0px; + width: 20px; + height: 20px; + content: ""; + background-color: transparent; + border: 2px solid $aq-sea-foam; + border-radius: 50%; + display: block; + opacity: 0.7; + } + + &:after { + + position: absolute; + top: 7px; + left: 5px; + width: 10px; + height: 10px; + content: ""; + background-color: $aq-sea-foam; + //border: 1px solid #666; + border-radius: 50%; + //box-shadow: inset 1px 1px 1px #888; + display: block; + opacity: 0; + transition: 0.2s ease-out; + + } + + + + +} +.slick-dots li button:hover, +.slick-dots li button:focus +{ + outline: none; + &:after { + opacity: 1; + } +} + +.slick-dots li.slick-active button:after { + opacity: 1; +} + + + + diff --git a/docs/assets/images/homepage_hero_orb_03.png b/docs/assets/images/homepage_hero_orb_03.png new file mode 100644 index 0000000000000000000000000000000000000000..261f40f49073ff93d592c8f2dd687189e36badcd GIT binary patch literal 28423 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 \ No newline at end of file diff --git a/docs/assets/images/homepage_hero_terrain_08.svg b/docs/assets/images/homepage_hero_terrain_08.svg new file mode 100644 index 000000000000..fe2236c99569 --- /dev/null +++ b/docs/assets/images/homepage_hero_terrain_08.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/assets/images/homepage_v1_hero_person_01.png b/docs/assets/images/homepage_v1_hero_person_01.png new file mode 100644 index 0000000000000000000000000000000000000000..2901ed862e5d1c1010fa6156ef7015cffe830f6e GIT binary patch literal 56629 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

P`o`uZ9ZJV1*U?CfQ}gUzmRV#K2(pY zOFmQW@Q0GNaEGl4=%({}`qtRi!kwXKo*%`Fb#!&XX}QwU(n(4i z8Cx))xdMV69eoi*tHIUGG))UZR@-$}d+`vzLI4{s1rwGECf#yfV`9D&<)r17rt8|hpBJAW@OwS z7)b2eM`i|JUYq*^s@Gxs@{gtR^=f7Sb+o&37)4jXNY$f%O%tTr--AG%odo9@wEpSq z+xRcn!`@zDV=a7BW1W_?QKq@wYUGfa^C@py0<@cKFJqxiJ+RHnm+qmXZs+1f5jK4X zkUr>|94V1c3(hKmC*XYN_VI1EmQ{-EoMa08Nt-a<=K4mJDeKvKbXO$}pF0XjIk^E} zdgnT_2IJeVmi2=cwe2||V$&%6s_|q5eARVorr%LJX?2-^fo&v0H-Bws_}gtT{ZZus z^*N;Ooqi~Y1NBwzymk^Zo2)tA>6U`p9QOgG>G7~6`W0UmWl0damLVx8XSx0UyI)1x z--}%QDpscSd|lRmi(xNR(V}uoXXD7s8faR$&k|4D%uLqdZ6`GMX6K3~fMfUe6j0j< zn=^#_wLI5SyDG;YW|WmpmN)v2cEOXdn^s#`c3tE#dmq(`?SCb&+GlcVN&~(M$6WN8 zCwr>0IkiQHuk%box5Nlf&9@25{2RAGs`oHDyYOX!=b>n7>ST2ndw~1OT6c49oB97A5enCk literal 0 HcmV?d00001 diff --git a/docs/assets/images/trivy_logo_horizontal_white.svg b/docs/assets/images/trivy_logo_horizontal_white.svg new file mode 100644 index 000000000000..287eb199f1e8 --- /dev/null +++ b/docs/assets/images/trivy_logo_horizontal_white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/assets/javascripts/trivy_v1_homepage.js b/docs/assets/javascripts/trivy_v1_homepage.js new file mode 100644 index 000000000000..2fdfba9a8a00 --- /dev/null +++ b/docs/assets/javascripts/trivy_v1_homepage.js @@ -0,0 +1,9 @@ +/*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="

",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0Previous',nextArrow:'',autoplay:!1,autoplaySpeed:3e3,centerMode:!1,centerPadding:"50px",cssEase:"ease",customPaging:function(e,t){return i('