From da597c479cd69ef4c77137f6a27a812d78b7bb85 Mon Sep 17 00:00:00 2001 From: Teppei Fukuda Date: Wed, 3 Jan 2024 13:43:45 +0400 Subject: [PATCH 1/5] refactor: propagate time through context values (#5858) Signed-off-by: knqyf263 --- integration/client_server_test.go | 4 -- integration/integration_test.go | 11 +++-- integration/repo_test.go | 17 ++++--- .../testdata/debian-stretch.json.golden | 3 +- .../testdata/distroless-base.json.golden | 3 +- .../testdata/distroless-python27.json.golden | 3 +- .../ubuntu-1804-ignore-unfixed.json.golden | 3 +- integration/testdata/ubuntu-1804.json.golden | 3 +- pkg/clock/clock.go | 30 ++++++++---- pkg/cloud/aws/commands/run_test.go | 31 +++++++++---- pkg/cloud/report/report.go | 8 ++-- pkg/cloud/report/service_test.go | 15 ++++-- pkg/commands/server/run.go | 2 +- pkg/compliance/report/report.go | 7 +-- pkg/compliance/report/table.go | 5 +- pkg/compliance/report/table_test.go | 18 ++++++-- pkg/detector/ospkg/alma/alma.go | 31 ++----------- pkg/detector/ospkg/alma/alma_test.go | 13 +++--- pkg/detector/ospkg/alpine/alpine.go | 31 ++----------- pkg/detector/ospkg/alpine/alpine_test.go | 13 +++--- pkg/detector/ospkg/amazon/amazon.go | 31 ++----------- pkg/detector/ospkg/amazon/amazon_test.go | 13 +++--- pkg/detector/ospkg/chainguard/chainguard.go | 30 ++---------- pkg/detector/ospkg/debian/debian.go | 31 ++----------- pkg/detector/ospkg/debian/debian_test.go | 13 +++--- pkg/detector/ospkg/detect.go | 7 +-- pkg/detector/ospkg/mariner/mariner.go | 4 +- pkg/detector/ospkg/oracle/oracle.go | 12 ++--- pkg/detector/ospkg/oracle/oracle_test.go | 46 +++++++++---------- pkg/detector/ospkg/photon/photon.go | 31 ++----------- pkg/detector/ospkg/photon/photon_test.go | 13 +++--- pkg/detector/ospkg/redhat/redhat.go | 33 +++---------- pkg/detector/ospkg/redhat/redhat_test.go | 13 +++--- pkg/detector/ospkg/rocky/rocky.go | 31 ++----------- pkg/detector/ospkg/rocky/rocky_test.go | 13 +++--- pkg/detector/ospkg/suse/suse.go | 37 +++------------ pkg/detector/ospkg/suse/suse_test.go | 13 +++--- .../ubuntu/testdata/fixtures/ubuntu.yaml | 7 +++ pkg/detector/ospkg/ubuntu/ubuntu.go | 33 +++---------- pkg/detector/ospkg/ubuntu/ubuntu_test.go | 24 ++++------ pkg/detector/ospkg/version/version.go | 8 ++-- pkg/detector/ospkg/wolfi/wolfi.go | 30 ++---------- pkg/k8s/commands/run.go | 4 +- pkg/k8s/report/cyclonedx.go | 5 +- pkg/k8s/report/table.go | 17 +++++-- pkg/k8s/writer.go | 11 ++--- pkg/k8s/writer_test.go | 6 ++- pkg/report/cyclonedx/cyclonedx.go | 5 +- pkg/report/github/github.go | 5 +- pkg/report/github/github_test.go | 3 +- pkg/report/json.go | 3 +- pkg/report/json_test.go | 3 +- pkg/report/predicate/vuln.go | 6 +-- pkg/report/predicate/vuln_test.go | 6 +-- pkg/report/sarif.go | 3 +- pkg/report/sarif_test.go | 2 +- pkg/report/spdx/spdx.go | 5 +- pkg/report/table/table.go | 3 +- pkg/report/table/table_test.go | 2 +- pkg/report/template.go | 3 +- pkg/report/template_test.go | 8 ++-- pkg/report/writer.go | 10 ++-- pkg/result/filter.go | 2 +- pkg/result/filter_test.go | 4 +- pkg/result/ignore.go | 15 +++--- pkg/rpc/server/listen.go | 9 ++-- pkg/rpc/server/listen_test.go | 8 ++-- pkg/sbom/cyclonedx/core/cyclonedx.go | 9 ++-- pkg/sbom/cyclonedx/core/cyclonedx_test.go | 5 +- pkg/sbom/cyclonedx/marshal.go | 5 +- pkg/sbom/cyclonedx/marshal_test.go | 5 +- pkg/sbom/spdx/marshal.go | 5 +- pkg/sbom/spdx/marshal_test.go | 5 +- pkg/scanner/local/scan.go | 6 +-- pkg/scanner/ospkg/scan.go | 10 ++-- pkg/scanner/scan.go | 2 +- pkg/scanner/scan_test.go | 6 +-- 77 files changed, 384 insertions(+), 546 deletions(-) diff --git a/integration/client_server_test.go b/integration/client_server_test.go index c4274eb77a80..352fd8253444 100644 --- a/integration/client_server_test.go +++ b/integration/client_server_test.go @@ -17,7 +17,6 @@ import ( "github.com/stretchr/testify/require" testcontainers "github.com/testcontainers/testcontainers-go" - "github.com/aquasecurity/trivy/pkg/clock" "github.com/aquasecurity/trivy/pkg/report" "github.com/aquasecurity/trivy/pkg/uuid" ) @@ -364,8 +363,6 @@ func TestClientServerWithFormat(t *testing.T) { } fakeTime := time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC) - clock.SetFakeTime(t, fakeTime) - report.CustomTemplateFuncMap = map[string]interface{}{ "now": func() time.Time { return fakeTime @@ -428,7 +425,6 @@ func TestClientServerWithCycloneDX(t *testing.T) { addr, cacheDir := setup(t, setupOptions{}) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - clock.SetFakeTime(t, time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC)) uuid.SetFakeUUID(t, "3ff14136-e09f-4df9-80ea-%012d") osArgs, outputFile := setupClient(t, tt.args, addr, cacheDir, tt.golden) diff --git a/integration/integration_test.go b/integration/integration_test.go index 67dc9aeb49b5..05f9c094cd03 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -7,6 +7,7 @@ import ( "encoding/json" "flag" "fmt" + "github.com/aquasecurity/trivy/pkg/clock" "io" "net" "os" @@ -27,7 +28,6 @@ import ( "github.com/aquasecurity/trivy-db/pkg/db" "github.com/aquasecurity/trivy-db/pkg/metadata" - "github.com/aquasecurity/trivy/pkg/clock" "github.com/aquasecurity/trivy/pkg/commands" "github.com/aquasecurity/trivy/pkg/dbtest" "github.com/aquasecurity/trivy/pkg/types" @@ -44,8 +44,6 @@ func initDB(t *testing.T) string { entries, err := os.ReadDir(fixtureDir) require.NoError(t, err) - clock.SetFakeTime(t, time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC)) - var fixtures []string for _, entry := range entries { if entry.IsDir() { @@ -193,13 +191,16 @@ func readSpdxJson(t *testing.T, filePath string) *spdx.Document { } func execute(osArgs []string) error { + // Set a fake time + ctx := clock.With(context.Background(), time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC)) + // Setup CLI App app := commands.NewApp() app.SetOut(io.Discard) + app.SetArgs(osArgs) // Run Trivy - app.SetArgs(osArgs) - return app.Execute() + return app.ExecuteContext(ctx) } func compareReports(t *testing.T, wantFile, gotFile string, override func(*types.Report)) { diff --git a/integration/repo_test.go b/integration/repo_test.go index dbf9c9bcbdab..68321a57effe 100644 --- a/integration/repo_test.go +++ b/integration/repo_test.go @@ -4,16 +4,13 @@ package integration import ( "fmt" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "os" "path/filepath" "strings" "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/aquasecurity/trivy/pkg/clock" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/types" "github.com/aquasecurity/trivy/pkg/uuid" @@ -416,12 +413,15 @@ func TestRepository(t *testing.T) { osArgs := []string{ "-q", - "--cache-dir", cacheDir, + "--cache-dir", + cacheDir, command, "--skip-db-update", "--skip-policy-update", - "--format", string(format), - "--parallel", fmt.Sprint(tt.args.parallel), + "--format", + string(format), + "--parallel", + fmt.Sprint(tt.args.parallel), "--offline-scan", } @@ -499,7 +499,6 @@ func TestRepository(t *testing.T) { osArgs = append(osArgs, "--output", outputFile) osArgs = append(osArgs, tt.args.input) - clock.SetFakeTime(t, time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC)) uuid.SetFakeUUID(t, "3ff14136-e09f-4df9-80ea-%012d") // Run "trivy repo" diff --git a/integration/testdata/debian-stretch.json.golden b/integration/testdata/debian-stretch.json.golden index 6fd20bda1ee8..ed15dd42381f 100644 --- a/integration/testdata/debian-stretch.json.golden +++ b/integration/testdata/debian-stretch.json.golden @@ -6,8 +6,7 @@ "Metadata": { "OS": { "Family": "debian", - "Name": "9.9", - "EOSL": true + "Name": "9.9" }, "ImageID": "sha256:f26939cc87ef44a6fc554eedd0a976ab30b5bc2769d65d2e986b6c5f1fd4053d", "DiffIDs": [ diff --git a/integration/testdata/distroless-base.json.golden b/integration/testdata/distroless-base.json.golden index 82de2d5368b9..0bd390a36f54 100644 --- a/integration/testdata/distroless-base.json.golden +++ b/integration/testdata/distroless-base.json.golden @@ -6,8 +6,7 @@ "Metadata": { "OS": { "Family": "debian", - "Name": "9.9", - "EOSL": true + "Name": "9.9" }, "ImageID": "sha256:7f04a8d247173b1f2546d22913af637bbab4e7411e00ae6207da8d94c445750d", "DiffIDs": [ diff --git a/integration/testdata/distroless-python27.json.golden b/integration/testdata/distroless-python27.json.golden index f8d54b339867..8c0657976b97 100644 --- a/integration/testdata/distroless-python27.json.golden +++ b/integration/testdata/distroless-python27.json.golden @@ -6,8 +6,7 @@ "Metadata": { "OS": { "Family": "debian", - "Name": "9.9", - "EOSL": true + "Name": "9.9" }, "ImageID": "sha256:6fcac2cc8a710f21577b5bbd534e0bfc841c0cca569b57182ba19054696cddda", "DiffIDs": [ diff --git a/integration/testdata/ubuntu-1804-ignore-unfixed.json.golden b/integration/testdata/ubuntu-1804-ignore-unfixed.json.golden index fbc7dafbdbb7..f2a38510385b 100644 --- a/integration/testdata/ubuntu-1804-ignore-unfixed.json.golden +++ b/integration/testdata/ubuntu-1804-ignore-unfixed.json.golden @@ -6,8 +6,7 @@ "Metadata": { "OS": { "Family": "ubuntu", - "Name": "18.04", - "EOSL": true + "Name": "18.04" }, "ImageID": "sha256:a2a15febcdf362f6115e801d37b5e60d6faaeedcb9896155e5fe9d754025be12", "DiffIDs": [ diff --git a/integration/testdata/ubuntu-1804.json.golden b/integration/testdata/ubuntu-1804.json.golden index 93abad738729..5fc21dba6a6e 100644 --- a/integration/testdata/ubuntu-1804.json.golden +++ b/integration/testdata/ubuntu-1804.json.golden @@ -6,8 +6,7 @@ "Metadata": { "OS": { "Family": "ubuntu", - "Name": "18.04", - "EOSL": true + "Name": "18.04" }, "ImageID": "sha256:a2a15febcdf362f6115e801d37b5e60d6faaeedcb9896155e5fe9d754025be12", "DiffIDs": [ diff --git a/pkg/clock/clock.go b/pkg/clock/clock.go index e87f9ca8ede5..91f6a5212bd2 100644 --- a/pkg/clock/clock.go +++ b/pkg/clock/clock.go @@ -1,23 +1,33 @@ package clock import ( - "testing" + "context" "time" "k8s.io/utils/clock" clocktesting "k8s.io/utils/clock/testing" ) -var c clock.Clock = clock.RealClock{} +// clockKey is the context key for clock. It is unexported to prevent collisions with context keys defined in +// other packages. +type clockKey struct{} -// SetFakeTime sets a fake time for testing. -func SetFakeTime(t *testing.T, fakeTime time.Time) { - c = clocktesting.NewFakeClock(fakeTime) - t.Cleanup(func() { - c = clock.RealClock{} - }) +// With returns a new context with the given time. +func With(ctx context.Context, t time.Time) context.Context { + c := clocktesting.NewFakeClock(t) + return context.WithValue(ctx, clockKey{}, c) } -func Now() time.Time { - return c.Now() +// Now returns the current time. +func Now(ctx context.Context) time.Time { + return Clock(ctx).Now() +} + +// Clock returns the clock from the context. +func Clock(ctx context.Context) clock.Clock { + t, ok := ctx.Value(clockKey{}).(clock.Clock) + if !ok { + return clock.RealClock{} + } + return t } diff --git a/pkg/cloud/aws/commands/run_test.go b/pkg/cloud/aws/commands/run_test.go index 3c7a188f2d33..3d9d01f17292 100644 --- a/pkg/cloud/aws/commands/run_test.go +++ b/pkg/cloud/aws/commands/run_test.go @@ -1068,8 +1068,11 @@ Summary Report for compliance: my-custom-spec MisconfOptions: flag.MisconfOptions{IncludeNonFailures: true}, }, cacheContent: "testdata/s3andcloudtrailcache.json", - allServices: []string{"s3", "cloudtrail"}, - want: expectedS3AndCloudTrailResult, + allServices: []string{ + "s3", + "cloudtrail", + }, + want: expectedS3AndCloudTrailResult, }, { name: "skip certain services and include specific services", @@ -1087,7 +1090,10 @@ Summary Report for compliance: my-custom-spec MisconfOptions: flag.MisconfOptions{IncludeNonFailures: true}, }, cacheContent: "testdata/s3andcloudtrailcache.json", - allServices: []string{"s3", "cloudtrail"}, + allServices: []string{ + "s3", + "cloudtrail", + }, // we skip cloudtrail but still expect results from it as it is cached want: expectedS3AndCloudTrailResult, }, @@ -1096,16 +1102,23 @@ Summary Report for compliance: my-custom-spec options: flag.Options{ RegoOptions: flag.RegoOptions{SkipPolicyUpdate: true}, AWSOptions: flag.AWSOptions{ - Region: "us-east-1", - SkipServices: []string{"cloudtrail", "iam"}, - Account: "12345678", + Region: "us-east-1", + SkipServices: []string{ + "cloudtrail", + "iam", + }, + Account: "12345678", }, CloudOptions: flag.CloudOptions{ MaxCacheAge: time.Hour * 24 * 365 * 100, }, MisconfOptions: flag.MisconfOptions{IncludeNonFailures: true}, }, - allServices: []string{"s3", "cloudtrail", "iam"}, + allServices: []string{ + "s3", + "cloudtrail", + "iam", + }, cacheContent: "testdata/s3onlycache.json", want: expectedS3ScanResult, }, @@ -1129,7 +1142,7 @@ Summary Report for compliance: my-custom-spec }, } - clock.SetFakeTime(t, time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC)) + ctx := clock.With(context.Background(), time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC)) for _, test := range tests { t.Run(test.name, func(t *testing.T) { if test.allServices != nil { @@ -1179,7 +1192,7 @@ Summary Report for compliance: my-custom-spec require.NoError(t, os.WriteFile(cacheFile, cacheData, 0600)) } - err := Run(context.Background(), test.options) + err := Run(ctx, test.options) if test.expectErr { assert.Error(t, err) return diff --git a/pkg/cloud/report/report.go b/pkg/cloud/report/report.go index 8c4fa3861b8e..c60e00b45360 100644 --- a/pkg/cloud/report/report.go +++ b/pkg/cloud/report/report.go @@ -67,7 +67,7 @@ func Write(ctx context.Context, rep *Report, opt flag.Options, fromCache bool) e defer cleanup() if opt.Compliance.Spec.ID != "" { - return writeCompliance(rep, opt, output) + return writeCompliance(ctx, rep, opt, output) } var filtered []types.Result @@ -93,7 +93,7 @@ func Write(ctx context.Context, rep *Report, opt flag.Options, fromCache bool) e }) base := types.Report{ - CreatedAt: clock.Now(), + CreatedAt: clock.Now(ctx), ArtifactName: rep.AccountID, ArtifactType: ftypes.ArtifactAWSAccount, Results: filtered, @@ -139,7 +139,7 @@ func Write(ctx context.Context, rep *Report, opt flag.Options, fromCache bool) e } } -func writeCompliance(rep *Report, opt flag.Options, output io.Writer) error { +func writeCompliance(ctx context.Context, rep *Report, opt flag.Options, output io.Writer) error { var crr []types.Results for _, r := range rep.Results { crr = append(crr, r.Results) @@ -150,7 +150,7 @@ func writeCompliance(rep *Report, opt flag.Options, output io.Writer) error { return xerrors.Errorf("compliance report build error: %w", err) } - return cr.Write(complianceReport, cr.Option{ + return cr.Write(ctx, complianceReport, cr.Option{ Format: opt.Format, Report: opt.ReportFormat, Output: output, diff --git a/pkg/cloud/report/service_test.go b/pkg/cloud/report/service_test.go index 507f3ff31466..55dd6cf5f77d 100644 --- a/pkg/cloud/report/service_test.go +++ b/pkg/cloud/report/service_test.go @@ -88,7 +88,10 @@ This scan report was loaded from cached results. If you'd like to run a fresh sc }, }, AWSOptions: flag.AWSOptions{ - Services: []string{"s3", "ec2"}, + Services: []string{ + "s3", + "ec2", + }, }, }, fromCache: false, @@ -117,7 +120,11 @@ Scan Overview for AWS Account }, }, AWSOptions: flag.AWSOptions{ - Services: []string{"ec2", "s3", "iam"}, + Services: []string{ + "ec2", + "s3", + "iam", + }, }, }, fromCache: false, @@ -310,7 +317,7 @@ Scan Overview for AWS Account }`, }, } - clock.SetFakeTime(t, time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC)) + ctx := clock.With(context.Background(), time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC)) for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { report := New( @@ -323,7 +330,7 @@ Scan Overview for AWS Account output := bytes.NewBuffer(nil) tt.options.SetOutputWriter(output) - require.NoError(t, Write(context.Background(), report, tt.options, tt.fromCache)) + require.NoError(t, Write(ctx, report, tt.options, tt.fromCache)) assert.Equal(t, "AWS", report.Provider) assert.Equal(t, tt.options.AWSOptions.Account, report.AccountID) diff --git a/pkg/commands/server/run.go b/pkg/commands/server/run.go index 7ddb6f7b3abb..03b8f144170a 100644 --- a/pkg/commands/server/run.go +++ b/pkg/commands/server/run.go @@ -59,5 +59,5 @@ func Run(ctx context.Context, opts flag.Options) (err error) { server := rpcServer.NewServer(opts.AppVersion, opts.Listen, opts.CacheDir, opts.Token, opts.TokenHeader, opts.DBRepository, opts.RegistryOpts()) - return server.ListenAndServe(cache, opts.SkipDBUpdate) + return server.ListenAndServe(ctx, cache, opts.SkipDBUpdate) } diff --git a/pkg/compliance/report/report.go b/pkg/compliance/report/report.go index 8bc8b90d76ee..61a4973b2b0a 100644 --- a/pkg/compliance/report/report.go +++ b/pkg/compliance/report/report.go @@ -1,6 +1,7 @@ package report import ( + "context" "io" "golang.org/x/xerrors" @@ -63,8 +64,8 @@ type Writer interface { Write(ComplianceReport) error } -// Write writes the results in the give format -func Write(report *ComplianceReport, option Option) error { +// Write writes the results in the given format +func Write(ctx context.Context, report *ComplianceReport, option Option) error { switch option.Format { case types.FormatJSON: jwriter := JSONWriter{ @@ -79,7 +80,7 @@ func Write(report *ComplianceReport, option Option) error { Report: option.Report, Severities: option.Severities, } - err := complianceWriter.Write(report) + err := complianceWriter.Write(ctx, report) if err != nil { return err } diff --git a/pkg/compliance/report/table.go b/pkg/compliance/report/table.go index 5de2428a6062..c54f031ef3d6 100644 --- a/pkg/compliance/report/table.go +++ b/pkg/compliance/report/table.go @@ -1,6 +1,7 @@ package report import ( + "context" "io" "sync" @@ -26,7 +27,7 @@ const ( IssuesColumn = "Issues" ) -func (tw TableWriter) Write(report *ComplianceReport) error { +func (tw TableWriter) Write(ctx context.Context, report *ComplianceReport) error { switch tw.Report { case allReport: t := pkgReport.Writer{ @@ -36,7 +37,7 @@ func (tw TableWriter) Write(report *ComplianceReport) error { } for _, cr := range report.Results { r := types.Report{Results: cr.Results} - err := t.Write(r) + err := t.Write(ctx, r) if err != nil { return err } diff --git a/pkg/compliance/report/table_test.go b/pkg/compliance/report/table_test.go index b99aa4b0a20d..ec880adb518d 100644 --- a/pkg/compliance/report/table_test.go +++ b/pkg/compliance/report/table_test.go @@ -2,6 +2,7 @@ package report_test import ( "bytes" + "context" "os" "path/filepath" "testing" @@ -36,7 +37,10 @@ func TestTableWriter_Write(t *testing.T) { Results: types.Results{ { Misconfigurations: []types.DetectedMisconfiguration{ - {AVDID: "AVD-KSV012", Status: types.StatusFailure}, + { + AVDID: "AVD-KSV012", + Status: types.StatusFailure, + }, }, }, }, @@ -48,7 +52,10 @@ func TestTableWriter_Write(t *testing.T) { Results: types.Results{ { Misconfigurations: []types.DetectedMisconfiguration{ - {AVDID: "AVD-KSV013", Status: types.StatusFailure}, + { + AVDID: "AVD-KSV013", + Status: types.StatusFailure, + }, }, }, }, @@ -62,8 +69,11 @@ func TestTableWriter_Write(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { buf := new(bytes.Buffer) - tr := report.TableWriter{Report: tt.reportType, Output: buf} - err := tr.Write(tt.input) + tr := report.TableWriter{ + Report: tt.reportType, + Output: buf, + } + err := tr.Write(context.Background(), tt.input) require.NoError(t, err) want, err := os.ReadFile(tt.want) diff --git a/pkg/detector/ospkg/alma/alma.go b/pkg/detector/ospkg/alma/alma.go index a9450046f27a..67465f04a1b0 100644 --- a/pkg/detector/ospkg/alma/alma.go +++ b/pkg/detector/ospkg/alma/alma.go @@ -1,12 +1,12 @@ package alma import ( + "context" "strings" "time" version "github.com/knqyf263/go-rpm-version" "golang.org/x/xerrors" - "k8s.io/utils/clock" "github.com/aquasecurity/trivy-db/pkg/vulnsrc/alma" osver "github.com/aquasecurity/trivy/pkg/detector/ospkg/version" @@ -25,36 +25,15 @@ var ( } ) -type options struct { - clock clock.Clock -} - -type option func(*options) - -func WithClock(c clock.Clock) option { - return func(opts *options) { - opts.clock = c - } -} - // Scanner implements the AlmaLinux scanner type Scanner struct { vs *alma.VulnSrc - *options } // NewScanner is the factory method for Scanner -func NewScanner(opts ...option) *Scanner { - o := &options{ - clock: clock.RealClock{}, - } - - for _, opt := range opts { - opt(o) - } +func NewScanner() *Scanner { return &Scanner{ - vs: alma.NewVulnSrc(), - options: o, + vs: alma.NewVulnSrc(), } } @@ -107,8 +86,8 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa } // IsSupportedVersion checks if the version is supported. -func (s *Scanner) IsSupportedVersion(osFamily ftypes.OSType, osVer string) bool { - return osver.Supported(s.clock, eolDates, osFamily, osver.Major(osVer)) +func (s *Scanner) IsSupportedVersion(ctx context.Context, osFamily ftypes.OSType, osVer string) bool { + return osver.Supported(ctx, eolDates, osFamily, osver.Major(osVer)) } func addModularNamespace(name, label string) string { diff --git a/pkg/detector/ospkg/alma/alma_test.go b/pkg/detector/ospkg/alma/alma_test.go index 5934e9a29779..bd70079fd189 100644 --- a/pkg/detector/ospkg/alma/alma_test.go +++ b/pkg/detector/ospkg/alma/alma_test.go @@ -1,13 +1,11 @@ package alma_test import ( + "context" + "github.com/aquasecurity/trivy/pkg/clock" "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - fake "k8s.io/utils/clock/testing" - "github.com/aquasecurity/trivy-db/pkg/db" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" "github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability" @@ -15,6 +13,8 @@ import ( "github.com/aquasecurity/trivy/pkg/detector/ospkg/alma" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestScanner_Detect(t *testing.T) { @@ -215,8 +215,9 @@ func TestScanner_IsSupportedVersion(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := alma.NewScanner(alma.WithClock(fake.NewFakeClock(tt.now))) - got := s.IsSupportedVersion(tt.args.osFamily, tt.args.osVer) + ctx := clock.With(context.Background(), tt.now) + s := alma.NewScanner() + got := s.IsSupportedVersion(ctx, tt.args.osFamily, tt.args.osVer) assert.Equal(t, tt.want, got) }) } diff --git a/pkg/detector/ospkg/alpine/alpine.go b/pkg/detector/ospkg/alpine/alpine.go index f1d284fea15e..4602df844727 100644 --- a/pkg/detector/ospkg/alpine/alpine.go +++ b/pkg/detector/ospkg/alpine/alpine.go @@ -1,12 +1,12 @@ package alpine import ( + "context" "strings" "time" version "github.com/knqyf263/go-apk-version" "golang.org/x/xerrors" - "k8s.io/utils/clock" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" "github.com/aquasecurity/trivy-db/pkg/vulnsrc/alpine" @@ -50,36 +50,15 @@ var ( } ) -type options struct { - clock clock.Clock -} - -type option func(*options) - -func WithClock(c clock.Clock) option { - return func(opts *options) { - opts.clock = c - } -} - // Scanner implements the Alpine scanner type Scanner struct { vs alpine.VulnSrc - *options } // NewScanner is the factory method for Scanner -func NewScanner(opts ...option) *Scanner { - o := &options{ - clock: clock.RealClock{}, - } - - for _, opt := range opts { - opt(o) - } +func NewScanner() *Scanner { return &Scanner{ - vs: alpine.NewVulnSrc(), - options: o, + vs: alpine.NewVulnSrc(), } } @@ -173,8 +152,8 @@ func (s *Scanner) isVulnerable(installedVersion version.Version, adv dbTypes.Adv } // IsSupportedVersion checks if the version is supported. -func (s *Scanner) IsSupportedVersion(osFamily ftypes.OSType, osVer string) bool { - return osver.Supported(s.clock, eolDates, osFamily, osver.Minor(osVer)) +func (s *Scanner) IsSupportedVersion(ctx context.Context, osFamily ftypes.OSType, osVer string) bool { + return osver.Supported(ctx, eolDates, osFamily, osver.Minor(osVer)) } func (s *Scanner) repoRelease(repo *ftypes.Repository) string { diff --git a/pkg/detector/ospkg/alpine/alpine_test.go b/pkg/detector/ospkg/alpine/alpine_test.go index 35abfc1f6e98..f420cf5576ab 100644 --- a/pkg/detector/ospkg/alpine/alpine_test.go +++ b/pkg/detector/ospkg/alpine/alpine_test.go @@ -1,21 +1,21 @@ package alpine_test import ( + "context" "sort" "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - fake "k8s.io/utils/clock/testing" - "github.com/aquasecurity/trivy-db/pkg/db" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" "github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability" + "github.com/aquasecurity/trivy/pkg/clock" "github.com/aquasecurity/trivy/pkg/dbtest" "github.com/aquasecurity/trivy/pkg/detector/ospkg/alpine" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestScanner_Detect(t *testing.T) { @@ -326,8 +326,9 @@ func TestScanner_IsSupportedVersion(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := alpine.NewScanner(alpine.WithClock(fake.NewFakeClock(tt.now))) - got := s.IsSupportedVersion(tt.args.osFamily, tt.args.osVer) + ctx := clock.With(context.Background(), tt.now) + s := alpine.NewScanner() + got := s.IsSupportedVersion(ctx, tt.args.osFamily, tt.args.osVer) assert.Equal(t, tt.want, got) }) } diff --git a/pkg/detector/ospkg/amazon/amazon.go b/pkg/detector/ospkg/amazon/amazon.go index 8bb310b131c7..0846e24c3dcb 100644 --- a/pkg/detector/ospkg/amazon/amazon.go +++ b/pkg/detector/ospkg/amazon/amazon.go @@ -1,12 +1,12 @@ package amazon import ( + "context" "strings" "time" version "github.com/knqyf263/go-deb-version" "golang.org/x/xerrors" - "k8s.io/utils/clock" "github.com/aquasecurity/trivy-db/pkg/vulnsrc/amazon" osver "github.com/aquasecurity/trivy/pkg/detector/ospkg/version" @@ -27,36 +27,15 @@ var ( } ) -type options struct { - clock clock.Clock -} - -type option func(*options) - -func WithClock(c clock.Clock) option { - return func(opts *options) { - opts.clock = c - } -} - // Scanner to scan amazon vulnerabilities type Scanner struct { ac amazon.VulnSrc - options } // NewScanner is the factory method to return Amazon scanner -func NewScanner(opts ...option) *Scanner { - o := &options{ - clock: clock.RealClock{}, - } - - for _, opt := range opts { - opt(o) - } +func NewScanner() *Scanner { return &Scanner{ - ac: amazon.NewVulnSrc(), - options: *o, + ac: amazon.NewVulnSrc(), } } @@ -116,11 +95,11 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa } // IsSupportedVersion checks if the version is supported. -func (s *Scanner) IsSupportedVersion(osFamily ftypes.OSType, osVer string) bool { +func (s *Scanner) IsSupportedVersion(ctx context.Context, osFamily ftypes.OSType, osVer string) bool { osVer = strings.Fields(osVer)[0] if osVer != "2" && osVer != "2022" && osVer != "2023" { osVer = "1" } - return osver.Supported(s.clock, eolDates, osFamily, osVer) + return osver.Supported(ctx, eolDates, osFamily, osVer) } diff --git a/pkg/detector/ospkg/amazon/amazon_test.go b/pkg/detector/ospkg/amazon/amazon_test.go index 2839adc32e19..1fa65f0598fb 100644 --- a/pkg/detector/ospkg/amazon/amazon_test.go +++ b/pkg/detector/ospkg/amazon/amazon_test.go @@ -1,13 +1,11 @@ package amazon_test import ( + "context" + "github.com/aquasecurity/trivy/pkg/clock" "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - fake "k8s.io/utils/clock/testing" - "github.com/aquasecurity/trivy-db/pkg/db" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" "github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability" @@ -15,6 +13,8 @@ import ( "github.com/aquasecurity/trivy/pkg/detector/ospkg/amazon" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestScanner_Detect(t *testing.T) { @@ -248,8 +248,9 @@ func TestScanner_IsSupportedVersion(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := amazon.NewScanner(amazon.WithClock(fake.NewFakeClock(tt.now))) - got := s.IsSupportedVersion(tt.args.osFamily, tt.args.osVer) + ctx := clock.With(context.Background(), tt.now) + s := amazon.NewScanner() + got := s.IsSupportedVersion(ctx, tt.args.osFamily, tt.args.osVer) assert.Equal(t, tt.want, got) }) } diff --git a/pkg/detector/ospkg/chainguard/chainguard.go b/pkg/detector/ospkg/chainguard/chainguard.go index f2ef2c307034..bba9642c481c 100644 --- a/pkg/detector/ospkg/chainguard/chainguard.go +++ b/pkg/detector/ospkg/chainguard/chainguard.go @@ -1,9 +1,10 @@ package chainguard import ( + "context" + version "github.com/knqyf263/go-apk-version" "golang.org/x/xerrors" - "k8s.io/utils/clock" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" "github.com/aquasecurity/trivy-db/pkg/vulnsrc/chainguard" @@ -13,36 +14,15 @@ import ( "github.com/aquasecurity/trivy/pkg/types" ) -type options struct { - clock clock.Clock -} - -type option func(*options) - -func WithClock(c clock.Clock) option { - return func(opts *options) { - opts.clock = c - } -} - // Scanner implements the Chainguard scanner type Scanner struct { vs chainguard.VulnSrc - *options } // NewScanner is the factory method for Scanner -func NewScanner(opts ...option) *Scanner { - o := &options{ - clock: clock.RealClock{}, - } - - for _, opt := range opts { - opt(o) - } +func NewScanner() *Scanner { return &Scanner{ - vs: chainguard.NewVulnSrc(), - options: o, + vs: chainguard.NewVulnSrc(), } } @@ -103,7 +83,7 @@ func (s *Scanner) isVulnerable(installedVersion version.Version, adv dbTypes.Adv } // IsSupportedVersion checks if the version is supported. -func (s *Scanner) IsSupportedVersion(_ ftypes.OSType, _ string) bool { +func (s *Scanner) IsSupportedVersion(_ context.Context, _ ftypes.OSType, _ string) bool { // Chainguard doesn't have versions, so there is no case where a given input yields a // result of an unsupported Chainguard version. diff --git a/pkg/detector/ospkg/debian/debian.go b/pkg/detector/ospkg/debian/debian.go index b0a1d426f4d4..c350f6d2c281 100644 --- a/pkg/detector/ospkg/debian/debian.go +++ b/pkg/detector/ospkg/debian/debian.go @@ -1,11 +1,11 @@ package debian import ( + "context" "time" version "github.com/knqyf263/go-deb-version" "golang.org/x/xerrors" - "k8s.io/utils/clock" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" "github.com/aquasecurity/trivy-db/pkg/vulnsrc/debian" @@ -41,36 +41,15 @@ var ( } ) -type options struct { - clock clock.Clock -} - -type option func(*options) - -func WithClock(c clock.Clock) option { - return func(opts *options) { - opts.clock = c - } -} - // Scanner implements the Debian scanner type Scanner struct { vs debian.VulnSrc - *options } // NewScanner is the factory method to return Scanner -func NewScanner(opts ...option) *Scanner { - o := &options{ - clock: clock.RealClock{}, - } - - for _, opt := range opts { - opt(o) - } +func NewScanner() *Scanner { return &Scanner{ - vs: debian.NewVulnSrc(), - options: o, + vs: debian.NewVulnSrc(), } } @@ -140,6 +119,6 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa } // IsSupportedVersion checks if the version is supported. -func (s *Scanner) IsSupportedVersion(osFamily ftypes.OSType, osVer string) bool { - return osver.Supported(s.clock, eolDates, osFamily, osver.Major(osVer)) +func (s *Scanner) IsSupportedVersion(ctx context.Context, osFamily ftypes.OSType, osVer string) bool { + return osver.Supported(ctx, eolDates, osFamily, osver.Major(osVer)) } diff --git a/pkg/detector/ospkg/debian/debian_test.go b/pkg/detector/ospkg/debian/debian_test.go index 9d5e847e2656..8c22a386a74d 100644 --- a/pkg/detector/ospkg/debian/debian_test.go +++ b/pkg/detector/ospkg/debian/debian_test.go @@ -1,14 +1,12 @@ package debian_test import ( + "context" + "github.com/aquasecurity/trivy/pkg/clock" "sort" "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - fake "k8s.io/utils/clock/testing" - "github.com/aquasecurity/trivy-db/pkg/db" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" "github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability" @@ -16,6 +14,8 @@ import ( "github.com/aquasecurity/trivy/pkg/detector/ospkg/debian" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestScanner_Detect(t *testing.T) { @@ -172,8 +172,9 @@ func TestScanner_IsSupportedVersion(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := debian.NewScanner(debian.WithClock(fake.NewFakeClock(tt.now))) - got := s.IsSupportedVersion(tt.args.osFamily, tt.args.osVer) + ctx := clock.With(context.Background(), tt.now) + s := debian.NewScanner() + got := s.IsSupportedVersion(ctx, tt.args.osFamily, tt.args.osVer) assert.Equal(t, tt.want, got) }) } diff --git a/pkg/detector/ospkg/detect.go b/pkg/detector/ospkg/detect.go index 16c3102265c4..32ed2ff9c5ab 100644 --- a/pkg/detector/ospkg/detect.go +++ b/pkg/detector/ospkg/detect.go @@ -1,6 +1,7 @@ package ospkg import ( + "context" "time" "github.com/samber/lo" @@ -55,17 +56,17 @@ func RegisterDriver(name ftypes.OSType, driver Driver) { // Driver defines operations for OS package scan type Driver interface { Detect(string, *ftypes.Repository, []ftypes.Package) ([]types.DetectedVulnerability, error) - IsSupportedVersion(ftypes.OSType, string) bool + IsSupportedVersion(context.Context, ftypes.OSType, string) bool } // Detect detects the vulnerabilities -func Detect(_, osFamily ftypes.OSType, osName string, repo *ftypes.Repository, _ time.Time, pkgs []ftypes.Package) ([]types.DetectedVulnerability, bool, error) { +func Detect(ctx context.Context, _, osFamily ftypes.OSType, osName string, repo *ftypes.Repository, _ time.Time, pkgs []ftypes.Package) ([]types.DetectedVulnerability, bool, error) { driver, err := newDriver(osFamily) if err != nil { return nil, false, ErrUnsupportedOS } - eosl := !driver.IsSupportedVersion(osFamily, osName) + eosl := !driver.IsSupportedVersion(ctx, osFamily, osName) // Package `gpg-pubkey` doesn't use the correct version. // We don't need to find vulnerabilities for this package. diff --git a/pkg/detector/ospkg/mariner/mariner.go b/pkg/detector/ospkg/mariner/mariner.go index 46cafa22fce6..6e1054151518 100644 --- a/pkg/detector/ospkg/mariner/mariner.go +++ b/pkg/detector/ospkg/mariner/mariner.go @@ -1,6 +1,8 @@ package mariner import ( + "context" + version "github.com/knqyf263/go-rpm-version" "golang.org/x/xerrors" @@ -73,7 +75,7 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa } // IsSupportedVersion checks if the version is supported. -func (s *Scanner) IsSupportedVersion(_ ftypes.OSType, _ string) bool { +func (s *Scanner) IsSupportedVersion(_ context.Context, _ ftypes.OSType, _ string) bool { // EOL is not in public at the moment. return true } diff --git a/pkg/detector/ospkg/oracle/oracle.go b/pkg/detector/ospkg/oracle/oracle.go index e8a34c95b07a..186e0d2734dc 100644 --- a/pkg/detector/ospkg/oracle/oracle.go +++ b/pkg/detector/ospkg/oracle/oracle.go @@ -1,12 +1,12 @@ package oracle import ( + "context" "strings" "time" version "github.com/knqyf263/go-rpm-version" "golang.org/x/xerrors" - "k8s.io/utils/clock" oracleoval "github.com/aquasecurity/trivy-db/pkg/vulnsrc/oracle-oval" osver "github.com/aquasecurity/trivy/pkg/detector/ospkg/version" @@ -33,15 +33,13 @@ var ( // Scanner implements oracle vulnerability scanner type Scanner struct { - vs *oracleoval.VulnSrc - clock clock.Clock + vs *oracleoval.VulnSrc } // NewScanner is the factory method to return oracle vulnerabilities func NewScanner() *Scanner { return &Scanner{ - vs: oracleoval.NewVulnSrc(), - clock: clock.RealClock{}, + vs: oracleoval.NewVulnSrc(), } } @@ -101,6 +99,6 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa } // IsSupportedVersion checks if the version is supported. -func (s *Scanner) IsSupportedVersion(osFamily ftypes.OSType, osVer string) bool { - return osver.Supported(s.clock, eolDates, osFamily, osver.Major(osVer)) +func (s *Scanner) IsSupportedVersion(ctx context.Context, osFamily ftypes.OSType, osVer string) bool { + return osver.Supported(ctx, eolDates, osFamily, osver.Major(osVer)) } diff --git a/pkg/detector/ospkg/oracle/oracle_test.go b/pkg/detector/ospkg/oracle/oracle_test.go index e72bc1c1ed2d..40c8f26f2d9a 100644 --- a/pkg/detector/ospkg/oracle/oracle_test.go +++ b/pkg/detector/ospkg/oracle/oracle_test.go @@ -1,95 +1,91 @@ package oracle import ( + "context" + "github.com/aquasecurity/trivy/pkg/clock" "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "k8s.io/utils/clock" - clocktesting "k8s.io/utils/clock/testing" - "github.com/aquasecurity/trivy-db/pkg/db" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" - oracleoval "github.com/aquasecurity/trivy-db/pkg/vulnsrc/oracle-oval" "github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability" "github.com/aquasecurity/trivy/pkg/dbtest" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestScanner_IsSupportedVersion(t *testing.T) { - vectors := map[string]struct { - clock clock.Clock + tests := map[string]struct { + now time.Time osFamily ftypes.OSType osVersion string expected bool }{ "oracle3": { - clock: clocktesting.NewFakeClock(time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC)), + now: time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC), osFamily: "oracle", osVersion: "3", expected: false, }, "oracle4": { - clock: clocktesting.NewFakeClock(time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC)), + now: time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC), osFamily: "oracle", osVersion: "4", expected: false, }, "oracle5": { - clock: clocktesting.NewFakeClock(time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC)), + now: time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC), osFamily: "oracle", osVersion: "5", expected: false, }, "oracle6": { - clock: clocktesting.NewFakeClock(time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC)), + now: time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC), osFamily: "oracle", osVersion: "6", expected: true, }, "oracle7": { - clock: clocktesting.NewFakeClock(time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC)), + now: time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC), osFamily: "oracle", osVersion: "7", expected: true, }, "oracle7.6": { - clock: clocktesting.NewFakeClock(time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC)), + now: time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC), osFamily: "oracle", osVersion: "7.6", expected: true, }, "oracle8": { - clock: clocktesting.NewFakeClock(time.Date(2029, 7, 18, 23, 59, 58, 59, time.UTC)), + now: time.Date(2029, 7, 18, 23, 59, 58, 59, time.UTC), osFamily: "oracle", osVersion: "8", expected: true, }, "oracle8-same-time": { - clock: clocktesting.NewFakeClock(time.Date(2029, 7, 18, 23, 59, 59, 0, time.UTC)), + now: time.Date(2029, 7, 18, 23, 59, 59, 0, time.UTC), osFamily: "oracle", osVersion: "8", expected: false, }, "latest": { - clock: clocktesting.NewFakeClock(time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC)), + now: time.Date(2019, 5, 31, 23, 59, 59, 0, time.UTC), osFamily: "oracle", osVersion: "latest", expected: true, }, } - for testName, v := range vectors { - s := &Scanner{ - vs: oracleoval.NewVulnSrc(), - clock: v.clock, - } + for testName, tt := range tests { + s := NewScanner() t.Run(testName, func(t *testing.T) { - actual := s.IsSupportedVersion(v.osFamily, v.osVersion) - if actual != v.expected { - t.Errorf("[%s] got %v, want %v", testName, actual, v.expected) + ctx := clock.With(context.Background(), tt.now) + actual := s.IsSupportedVersion(ctx, tt.osFamily, tt.osVersion) + if actual != tt.expected { + t.Errorf("[%s] got %v, want %v", testName, actual, tt.expected) } }) } diff --git a/pkg/detector/ospkg/photon/photon.go b/pkg/detector/ospkg/photon/photon.go index 558e5511fc2f..b6c00f3c24f8 100644 --- a/pkg/detector/ospkg/photon/photon.go +++ b/pkg/detector/ospkg/photon/photon.go @@ -1,11 +1,11 @@ package photon import ( + "context" "time" version "github.com/knqyf263/go-rpm-version" "golang.org/x/xerrors" - "k8s.io/utils/clock" "github.com/aquasecurity/trivy-db/pkg/vulnsrc/photon" osver "github.com/aquasecurity/trivy/pkg/detector/ospkg/version" @@ -26,36 +26,15 @@ var ( } ) -type options struct { - clock clock.Clock -} - -type option func(*options) - -func WithClock(c clock.Clock) option { - return func(opts *options) { - opts.clock = c - } -} - // Scanner implements the Photon scanner type Scanner struct { vs photon.VulnSrc - *options } // NewScanner is the factory method for Scanner -func NewScanner(opts ...option) *Scanner { - o := &options{ - clock: clock.RealClock{}, - } - - for _, opt := range opts { - opt(o) - } +func NewScanner() *Scanner { return &Scanner{ - vs: photon.NewVulnSrc(), - options: o, + vs: photon.NewVulnSrc(), } } @@ -96,6 +75,6 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa } // IsSupportedVersion checks if the version is supported. -func (s *Scanner) IsSupportedVersion(osFamily ftypes.OSType, osVer string) bool { - return osver.Supported(s.clock, eolDates, osFamily, osVer) +func (s *Scanner) IsSupportedVersion(ctx context.Context, osFamily ftypes.OSType, osVer string) bool { + return osver.Supported(ctx, eolDates, osFamily, osVer) } diff --git a/pkg/detector/ospkg/photon/photon_test.go b/pkg/detector/ospkg/photon/photon_test.go index 84d1e947880d..b81b0fd30d6d 100644 --- a/pkg/detector/ospkg/photon/photon_test.go +++ b/pkg/detector/ospkg/photon/photon_test.go @@ -1,13 +1,11 @@ package photon_test import ( + "context" + "github.com/aquasecurity/trivy/pkg/clock" "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - fake "k8s.io/utils/clock/testing" - "github.com/aquasecurity/trivy-db/pkg/db" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" "github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability" @@ -15,6 +13,8 @@ import ( "github.com/aquasecurity/trivy/pkg/detector/ospkg/photon" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestScanner_Detect(t *testing.T) { @@ -147,8 +147,9 @@ func TestScanner_IsSupportedVersion(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := photon.NewScanner(photon.WithClock(fake.NewFakeClock(tt.now))) - got := s.IsSupportedVersion(tt.args.osFamily, tt.args.osVer) + ctx := clock.With(context.Background(), tt.now) + s := photon.NewScanner() + got := s.IsSupportedVersion(ctx, tt.args.osFamily, tt.args.osVer) assert.Equal(t, tt.want, got) }) } diff --git a/pkg/detector/ospkg/redhat/redhat.go b/pkg/detector/ospkg/redhat/redhat.go index 54bedcbf98c3..fe7581dbf481 100644 --- a/pkg/detector/ospkg/redhat/redhat.go +++ b/pkg/detector/ospkg/redhat/redhat.go @@ -1,6 +1,7 @@ package redhat import ( + "context" "fmt" "sort" "strings" @@ -10,7 +11,6 @@ import ( "golang.org/x/exp/maps" "golang.org/x/exp/slices" "golang.org/x/xerrors" - "k8s.io/utils/clock" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" ustrings "github.com/aquasecurity/trivy-db/pkg/utils/strings" @@ -64,36 +64,15 @@ var ( } ) -type options struct { - clock clock.Clock -} - -type option func(*options) - -func WithClock(c clock.Clock) option { - return func(opts *options) { - opts.clock = c - } -} - // Scanner implements the RedHat scanner type Scanner struct { vs redhat.VulnSrc - *options } // NewScanner is the factory method for Scanner -func NewScanner(opts ...option) *Scanner { - o := &options{ - clock: clock.RealClock{}, - } - - for _, opt := range opts { - opt(o) - } +func NewScanner() *Scanner { return &Scanner{ - vs: redhat.NewVulnSrc(), - options: o, + vs: redhat.NewVulnSrc(), } } @@ -208,13 +187,13 @@ func (s *Scanner) detect(osVer string, pkg ftypes.Package) ([]types.DetectedVuln } // IsSupportedVersion checks is OSFamily can be scanned with Redhat scanner -func (s *Scanner) IsSupportedVersion(osFamily ftypes.OSType, osVer string) bool { +func (s *Scanner) IsSupportedVersion(ctx context.Context, osFamily ftypes.OSType, osVer string) bool { osVer = osver.Major(osVer) if osFamily == ftypes.CentOS { - return osver.Supported(s.clock, centosEOLDates, osFamily, osVer) + return osver.Supported(ctx, centosEOLDates, osFamily, osVer) } - return osver.Supported(s.clock, redhatEOLDates, osFamily, osVer) + return osver.Supported(ctx, redhatEOLDates, osFamily, osVer) } func isFromSupportedVendor(pkg ftypes.Package) bool { diff --git a/pkg/detector/ospkg/redhat/redhat_test.go b/pkg/detector/ospkg/redhat/redhat_test.go index 034e37a8e40c..3910b87f9cac 100644 --- a/pkg/detector/ospkg/redhat/redhat_test.go +++ b/pkg/detector/ospkg/redhat/redhat_test.go @@ -1,14 +1,12 @@ package redhat_test import ( + "context" + "github.com/aquasecurity/trivy/pkg/clock" "os" "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - fake "k8s.io/utils/clock/testing" - dbTypes "github.com/aquasecurity/trivy-db/pkg/types" "github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability" "github.com/aquasecurity/trivy/pkg/dbtest" @@ -16,6 +14,8 @@ import ( ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestMain(m *testing.M) { @@ -435,8 +435,9 @@ func TestScanner_IsSupportedVersion(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := redhat.NewScanner(redhat.WithClock(fake.NewFakeClock(tt.now))) - got := s.IsSupportedVersion(tt.args.osFamily, tt.args.osVer) + ctx := clock.With(context.Background(), tt.now) + s := redhat.NewScanner() + got := s.IsSupportedVersion(ctx, tt.args.osFamily, tt.args.osVer) assert.Equal(t, tt.want, got) }) } diff --git a/pkg/detector/ospkg/rocky/rocky.go b/pkg/detector/ospkg/rocky/rocky.go index 5de786675b34..49aaa4d0a543 100644 --- a/pkg/detector/ospkg/rocky/rocky.go +++ b/pkg/detector/ospkg/rocky/rocky.go @@ -1,11 +1,11 @@ package rocky import ( + "context" "time" version "github.com/knqyf263/go-rpm-version" "golang.org/x/xerrors" - "k8s.io/utils/clock" "github.com/aquasecurity/trivy-db/pkg/vulnsrc/rocky" osver "github.com/aquasecurity/trivy/pkg/detector/ospkg/version" @@ -24,36 +24,15 @@ var ( } ) -type options struct { - clock clock.Clock -} - -type option func(*options) - -func WithClock(c clock.Clock) option { - return func(opts *options) { - opts.clock = c - } -} - // Scanner implements the Rocky Linux scanner type Scanner struct { vs *rocky.VulnSrc - *options } // NewScanner is the factory method for Scanner -func NewScanner(opts ...option) *Scanner { - o := &options{ - clock: clock.RealClock{}, - } - - for _, opt := range opts { - opt(o) - } +func NewScanner() *Scanner { return &Scanner{ - vs: rocky.NewVulnSrc(), - options: o, + vs: rocky.NewVulnSrc(), } } @@ -107,8 +86,8 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa } // IsSupportedVersion checks if the version is supported. -func (s *Scanner) IsSupportedVersion(osFamily ftypes.OSType, osVer string) bool { - return osver.Supported(s.clock, eolDates, osFamily, osver.Major(osVer)) +func (s *Scanner) IsSupportedVersion(ctx context.Context, osFamily ftypes.OSType, osVer string) bool { + return osver.Supported(ctx, eolDates, osFamily, osver.Major(osVer)) } func addModularNamespace(name, label string) string { diff --git a/pkg/detector/ospkg/rocky/rocky_test.go b/pkg/detector/ospkg/rocky/rocky_test.go index 4a4c68f047d6..dddba1df850a 100644 --- a/pkg/detector/ospkg/rocky/rocky_test.go +++ b/pkg/detector/ospkg/rocky/rocky_test.go @@ -1,13 +1,11 @@ package rocky_test import ( + "context" + "github.com/aquasecurity/trivy/pkg/clock" "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - fake "k8s.io/utils/clock/testing" - "github.com/aquasecurity/trivy-db/pkg/db" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" "github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability" @@ -15,6 +13,8 @@ import ( "github.com/aquasecurity/trivy/pkg/detector/ospkg/rocky" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestScanner_Detect(t *testing.T) { @@ -175,8 +175,9 @@ func TestScanner_IsSupportedVersion(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := rocky.NewScanner(rocky.WithClock(fake.NewFakeClock(tt.now))) - got := s.IsSupportedVersion(tt.args.osFamily, tt.args.osVer) + ctx := clock.With(context.Background(), tt.now) + s := rocky.NewScanner() + got := s.IsSupportedVersion(ctx, tt.args.osFamily, tt.args.osVer) assert.Equal(t, tt.want, got) }) } diff --git a/pkg/detector/ospkg/suse/suse.go b/pkg/detector/ospkg/suse/suse.go index 9b685a50c448..617161df87aa 100644 --- a/pkg/detector/ospkg/suse/suse.go +++ b/pkg/detector/ospkg/suse/suse.go @@ -1,11 +1,11 @@ package suse import ( + "context" "time" version "github.com/knqyf263/go-rpm-version" "golang.org/x/xerrors" - "k8s.io/utils/clock" susecvrf "github.com/aquasecurity/trivy-db/pkg/vulnsrc/suse-cvrf" osver "github.com/aquasecurity/trivy/pkg/detector/ospkg/version" @@ -58,18 +58,6 @@ var ( } ) -type options struct { - clock clock.Clock -} - -type option func(*options) - -func WithClock(c clock.Clock) option { - return func(opts *options) { - opts.clock = c - } -} - // Type defines SUSE type type Type int @@ -83,29 +71,18 @@ const ( // Scanner implements the SUSE scanner type Scanner struct { vs susecvrf.VulnSrc - *options } // NewScanner is the factory method for Scanner -func NewScanner(t Type, opts ...option) *Scanner { - o := &options{ - clock: clock.RealClock{}, - } - - for _, opt := range opts { - opt(o) - } - +func NewScanner(t Type) *Scanner { switch t { case SUSEEnterpriseLinux: return &Scanner{ - vs: susecvrf.NewVulnSrc(susecvrf.SUSEEnterpriseLinux), - options: o, + vs: susecvrf.NewVulnSrc(susecvrf.SUSEEnterpriseLinux), } case OpenSUSE: return &Scanner{ - vs: susecvrf.NewVulnSrc(susecvrf.OpenSUSE), - options: o, + vs: susecvrf.NewVulnSrc(susecvrf.OpenSUSE), } } return nil @@ -148,9 +125,9 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa } // IsSupportedVersion checks if OSFamily can be scanned using SUSE scanner -func (s *Scanner) IsSupportedVersion(osFamily ftypes.OSType, osVer string) bool { +func (s *Scanner) IsSupportedVersion(ctx context.Context, osFamily ftypes.OSType, osVer string) bool { if osFamily == ftypes.SLES { - return osver.Supported(s.clock, slesEolDates, osFamily, osVer) + return osver.Supported(ctx, slesEolDates, osFamily, osVer) } - return osver.Supported(s.clock, opensuseEolDates, osFamily, osVer) + return osver.Supported(ctx, opensuseEolDates, osFamily, osVer) } diff --git a/pkg/detector/ospkg/suse/suse_test.go b/pkg/detector/ospkg/suse/suse_test.go index 85d0024335be..7766863e2ec4 100644 --- a/pkg/detector/ospkg/suse/suse_test.go +++ b/pkg/detector/ospkg/suse/suse_test.go @@ -1,13 +1,11 @@ package suse_test import ( + "context" + "github.com/aquasecurity/trivy/pkg/clock" "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - fake "k8s.io/utils/clock/testing" - "github.com/aquasecurity/trivy-db/pkg/db" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" "github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability" @@ -15,6 +13,8 @@ import ( "github.com/aquasecurity/trivy/pkg/detector/ospkg/suse" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestScanner_Detect(t *testing.T) { @@ -153,8 +153,9 @@ func TestScanner_IsSupportedVersion(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := suse.NewScanner(tt.distribution, suse.WithClock(fake.NewFakeClock(tt.now))) - got := s.IsSupportedVersion(tt.args.osFamily, tt.args.osVer) + ctx := clock.With(context.Background(), tt.now) + s := suse.NewScanner(tt.distribution) + got := s.IsSupportedVersion(ctx, tt.args.osFamily, tt.args.osVer) assert.Equal(t, tt.want, got) }) } diff --git a/pkg/detector/ospkg/ubuntu/testdata/fixtures/ubuntu.yaml b/pkg/detector/ospkg/ubuntu/testdata/fixtures/ubuntu.yaml index 7da30a16b22a..bcb207cd8e26 100644 --- a/pkg/detector/ospkg/ubuntu/testdata/fixtures/ubuntu.yaml +++ b/pkg/detector/ospkg/ubuntu/testdata/fixtures/ubuntu.yaml @@ -1,3 +1,10 @@ +- bucket: ubuntu 19.04 + pairs: + - bucket: wpa + pairs: + - key: CVE-2019-9243 + value: + FixedVersion: "" - bucket: ubuntu 20.04 pairs: - bucket: wpa diff --git a/pkg/detector/ospkg/ubuntu/ubuntu.go b/pkg/detector/ospkg/ubuntu/ubuntu.go index eb143fcd9952..46948806a64d 100644 --- a/pkg/detector/ospkg/ubuntu/ubuntu.go +++ b/pkg/detector/ospkg/ubuntu/ubuntu.go @@ -1,12 +1,12 @@ package ubuntu import ( + "context" "strings" "time" version "github.com/knqyf263/go-deb-version" "golang.org/x/xerrors" - "k8s.io/utils/clock" "github.com/aquasecurity/trivy-db/pkg/vulnsrc/ubuntu" osver "github.com/aquasecurity/trivy/pkg/detector/ospkg/version" @@ -63,36 +63,15 @@ var ( } ) -type options struct { - clock clock.Clock -} - -type option func(*options) - -func WithClock(c clock.Clock) option { - return func(opts *options) { - opts.clock = c - } -} - // Scanner implements the Ubuntu scanner type Scanner struct { vs ubuntu.VulnSrc - *options } // NewScanner is the factory method for Scanner -func NewScanner(opts ...option) *Scanner { - o := &options{ - clock: clock.RealClock{}, - } - - for _, opt := range opts { - opt(o) - } +func NewScanner() *Scanner { return &Scanner{ - vs: ubuntu.NewVulnSrc(), - options: o, + vs: ubuntu.NewVulnSrc(), } } @@ -149,8 +128,8 @@ func (s *Scanner) Detect(osVer string, _ *ftypes.Repository, pkgs []ftypes.Packa } // IsSupportedVersion checks is OSFamily can be scanned using Ubuntu scanner -func (s *Scanner) IsSupportedVersion(osFamily ftypes.OSType, osVer string) bool { - return osver.Supported(s.clock, eolDates, osFamily, osVer) +func (s *Scanner) IsSupportedVersion(ctx context.Context, osFamily ftypes.OSType, osVer string) bool { + return osver.Supported(ctx, eolDates, osFamily, osVer) } // versionFromEolDates checks if actual (not ESM) version is not outdated @@ -165,7 +144,7 @@ func (s *Scanner) versionFromEolDates(osVer string) string { // then we need to get vulnerabilities for `18.04` // if `18.04` is outdated - we need to use `18.04-ESM` (we will return error until we add `18.04-ESM` to eolDates) ver := strings.TrimRight(osVer, "-ESM") - if eol, ok := eolDates[ver]; ok && s.clock.Now().Before(eol) { + if eol, ok := eolDates[ver]; ok && time.Now().Before(eol) { // TODO: time.Now() should be replaced with clock.Now() return ver } return osVer diff --git a/pkg/detector/ospkg/ubuntu/ubuntu_test.go b/pkg/detector/ospkg/ubuntu/ubuntu_test.go index 96979fca7095..a2218e211b77 100644 --- a/pkg/detector/ospkg/ubuntu/ubuntu_test.go +++ b/pkg/detector/ospkg/ubuntu/ubuntu_test.go @@ -1,14 +1,12 @@ package ubuntu_test import ( + "context" + "github.com/aquasecurity/trivy/pkg/clock" "sort" "testing" "time" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - fake "k8s.io/utils/clock/testing" - "github.com/aquasecurity/trivy-db/pkg/db" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" "github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability" @@ -16,12 +14,13 @@ import ( "github.com/aquasecurity/trivy/pkg/detector/ospkg/ubuntu" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestScanner_Detect(t *testing.T) { type args struct { osVer string - now time.Time pkgs []ftypes.Package } tests := []struct { @@ -39,7 +38,6 @@ func TestScanner_Detect(t *testing.T) { }, args: args{ osVer: "20.04", - now: time.Date(2019, 3, 31, 23, 59, 59, 0, time.UTC), pkgs: []ftypes.Package{ { Name: "wpa", @@ -91,7 +89,6 @@ func TestScanner_Detect(t *testing.T) { }, args: args{ osVer: "20.04-ESM", - now: time.Date(2019, 3, 31, 23, 59, 59, 0, time.UTC), pkgs: []ftypes.Package{ { Name: "wpa", @@ -136,14 +133,13 @@ func TestScanner_Detect(t *testing.T) { }, }, { - name: "ubuntu 20.04-ESM. 20.04 is outdated", + name: "ubuntu 19.04-ESM, 19.04 is outdated", // Use 19.04-ESM for testing, although it doesn't exist fixtures: []string{ "testdata/fixtures/ubuntu.yaml", "testdata/fixtures/data-source.yaml", }, args: args{ - osVer: "20.04-ESM", - now: time.Date(2031, 3, 31, 23, 59, 59, 0, time.UTC), + osVer: "19.04-ESM", pkgs: []ftypes.Package{ { Name: "wpa", @@ -165,7 +161,6 @@ func TestScanner_Detect(t *testing.T) { }, args: args{ osVer: "21.04", - now: time.Date(2019, 3, 31, 23, 59, 59, 0, time.UTC), pkgs: []ftypes.Package{ { Name: "jq", @@ -183,7 +178,7 @@ func TestScanner_Detect(t *testing.T) { _ = dbtest.InitDB(t, tt.fixtures) defer db.Close() - s := ubuntu.NewScanner(ubuntu.WithClock(fake.NewFakeClock(tt.args.now))) + s := ubuntu.NewScanner() got, err := s.Detect(tt.args.osVer, nil, tt.args.pkgs) if tt.wantErr != "" { require.Error(t, err) @@ -258,8 +253,9 @@ func TestScanner_IsSupportedVersion(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - s := ubuntu.NewScanner(ubuntu.WithClock(fake.NewFakeClock(tt.now))) - got := s.IsSupportedVersion(tt.args.osFamily, tt.args.osVer) + ctx := clock.With(context.Background(), tt.now) + s := ubuntu.NewScanner() + got := s.IsSupportedVersion(ctx, tt.args.osFamily, tt.args.osVer) assert.Equal(t, tt.want, got) }) } diff --git a/pkg/detector/ospkg/version/version.go b/pkg/detector/ospkg/version/version.go index 2e0fce38a2ce..dc47ffd88409 100644 --- a/pkg/detector/ospkg/version/version.go +++ b/pkg/detector/ospkg/version/version.go @@ -1,11 +1,11 @@ package version import ( + "context" "strings" "time" - "k8s.io/utils/clock" - + "github.com/aquasecurity/trivy/pkg/clock" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/log" ) @@ -28,11 +28,11 @@ func Minor(osVer string) string { return major + "." + minor } -func Supported(c clock.Clock, eolDates map[string]time.Time, osFamily ftypes.OSType, osVer string) bool { +func Supported(ctx context.Context, eolDates map[string]time.Time, osFamily ftypes.OSType, osVer string) bool { eol, ok := eolDates[osVer] if !ok { log.Logger.Warnf("This OS version is not on the EOL list: %s %s", osFamily, osVer) return true // can be the latest version } - return c.Now().Before(eol) + return clock.Now(ctx).Before(eol) } diff --git a/pkg/detector/ospkg/wolfi/wolfi.go b/pkg/detector/ospkg/wolfi/wolfi.go index c4d8d71d972c..9757fc6aa637 100644 --- a/pkg/detector/ospkg/wolfi/wolfi.go +++ b/pkg/detector/ospkg/wolfi/wolfi.go @@ -1,9 +1,10 @@ package wolfi import ( + "context" + version "github.com/knqyf263/go-apk-version" "golang.org/x/xerrors" - "k8s.io/utils/clock" dbTypes "github.com/aquasecurity/trivy-db/pkg/types" "github.com/aquasecurity/trivy-db/pkg/vulnsrc/wolfi" @@ -13,36 +14,15 @@ import ( "github.com/aquasecurity/trivy/pkg/types" ) -type options struct { - clock clock.Clock -} - -type option func(*options) - -func WithClock(c clock.Clock) option { - return func(opts *options) { - opts.clock = c - } -} - // Scanner implements the Wolfi scanner type Scanner struct { vs wolfi.VulnSrc - *options } // NewScanner is the factory method for Scanner -func NewScanner(opts ...option) *Scanner { - o := &options{ - clock: clock.RealClock{}, - } - - for _, opt := range opts { - opt(o) - } +func NewScanner() *Scanner { return &Scanner{ - vs: wolfi.NewVulnSrc(), - options: o, + vs: wolfi.NewVulnSrc(), } } @@ -103,7 +83,7 @@ func (s *Scanner) isVulnerable(installedVersion version.Version, adv dbTypes.Adv } // IsSupportedVersion checks if the version is supported. -func (s *Scanner) IsSupportedVersion(_ ftypes.OSType, _ string) bool { +func (s *Scanner) IsSupportedVersion(_ context.Context, _ ftypes.OSType, _ string) bool { // Wolfi doesn't have versions, so there is no case where a given input yields a // result of an unsupported Wolfi version. diff --git a/pkg/k8s/commands/run.go b/pkg/k8s/commands/run.go index ec3e61385c65..61ea4860db61 100644 --- a/pkg/k8s/commands/run.go +++ b/pkg/k8s/commands/run.go @@ -116,14 +116,14 @@ func (r *runner) run(ctx context.Context, artifacts []*k8sArtifacts.Artifact) er if err != nil { return xerrors.Errorf("compliance report build error: %w", err) } - return cr.Write(complianceReport, cr.Option{ + return cr.Write(ctx, complianceReport, cr.Option{ Format: r.flagOpts.Format, Report: r.flagOpts.ReportFormat, Output: output, }) } - if err := k8sRep.Write(rpt, report.Option{ + if err := k8sRep.Write(ctx, rpt, report.Option{ Format: r.flagOpts.Format, Report: r.flagOpts.ReportFormat, Output: output, diff --git a/pkg/k8s/report/cyclonedx.go b/pkg/k8s/report/cyclonedx.go index 0640aed036a6..6e10d34e7e89 100644 --- a/pkg/k8s/report/cyclonedx.go +++ b/pkg/k8s/report/cyclonedx.go @@ -1,6 +1,7 @@ package report import ( + "context" "io" cdx "github.com/CycloneDX/cyclonedx-go" @@ -25,7 +26,7 @@ func NewCycloneDXWriter(output io.Writer, format cdx.BOMFileFormat, appVersion s } } -func (w CycloneDXWriter) Write(component *core.Component) error { - bom := w.marshaler.Marshal(component) +func (w CycloneDXWriter) Write(ctx context.Context, component *core.Component) error { + bom := w.marshaler.Marshal(ctx, component) return w.encoder.Encode(bom) } diff --git a/pkg/k8s/report/table.go b/pkg/k8s/report/table.go index f2cf548ca1a9..640d116cbd84 100644 --- a/pkg/k8s/report/table.go +++ b/pkg/k8s/report/table.go @@ -1,6 +1,7 @@ package report import ( + "context" "io" "sync" @@ -28,7 +29,11 @@ const ( ) func WorkloadColumns() []string { - return []string{VulnerabilitiesColumn, MisconfigurationsColumn, SecretsColumn} + return []string{ + VulnerabilitiesColumn, + MisconfigurationsColumn, + SecretsColumn, + } } func RoleColumns() []string { @@ -39,13 +44,17 @@ func InfraColumns() []string { return []string{InfraAssessmentColumn} } -func (tw TableWriter) Write(report Report) error { +func (tw TableWriter) Write(ctx context.Context, report Report) error { switch tw.Report { case AllReport: - t := pkgReport.Writer{Output: tw.Output, Severities: tw.Severities, ShowMessageOnce: &sync.Once{}} + t := pkgReport.Writer{ + Output: tw.Output, + Severities: tw.Severities, + ShowMessageOnce: &sync.Once{}, + } for _, r := range report.Resources { if r.Report.Results.Failed() { - err := t.Write(r.Report) + err := t.Write(ctx, r.Report) if err != nil { return err } diff --git a/pkg/k8s/writer.go b/pkg/k8s/writer.go index 4decab422699..0e218b38f30f 100644 --- a/pkg/k8s/writer.go +++ b/pkg/k8s/writer.go @@ -1,6 +1,7 @@ package k8s import ( + "context" "fmt" cdx "github.com/CycloneDX/cyclonedx-go" @@ -10,12 +11,8 @@ import ( "github.com/aquasecurity/trivy/pkg/types" ) -type Writer interface { - Write(report.Report) error -} - // Write writes the results in the give format -func Write(k8sreport report.Report, option report.Option) error { +func Write(ctx context.Context, k8sreport report.Report, option report.Option) error { k8sreport.PrintErrors() switch option.Format { @@ -41,7 +38,7 @@ func Write(k8sreport report.Report, option report.Option) error { ColumnHeading: report.ColumnHeading(option.Scanners, option.Components, r.Columns), } - if err := writer.Write(r.Report); err != nil { + if err := writer.Write(ctx, r.Report); err != nil { return err } } @@ -49,7 +46,7 @@ func Write(k8sreport report.Report, option report.Option) error { return nil case types.FormatCycloneDX: w := report.NewCycloneDXWriter(option.Output, cdx.BOMFileFormatJSON, option.APIVersion) - return w.Write(k8sreport.RootComponent) + return w.Write(ctx, k8sreport.RootComponent) } return nil } diff --git a/pkg/k8s/writer_test.go b/pkg/k8s/writer_test.go index d747b8867154..f8ab555b26c6 100644 --- a/pkg/k8s/writer_test.go +++ b/pkg/k8s/writer_test.go @@ -2,6 +2,8 @@ package k8s import ( "bytes" + "context" + "github.com/stretchr/testify/require" "regexp" "strings" "testing" @@ -393,8 +395,8 @@ Severities: C=CRITICAL H=HIGH M=MEDIUM L=LOW U=UNKNOWN`, Components: tc.components, } - Write(tc.report, opt) - + err := Write(context.Background(), tc.report, opt) + require.NoError(t, err) assert.Equal(t, tc.expectedOutput, stripAnsi(output.String()), tc.name) }) } diff --git a/pkg/report/cyclonedx/cyclonedx.go b/pkg/report/cyclonedx/cyclonedx.go index a99d2f6511db..f624c02e83b2 100644 --- a/pkg/report/cyclonedx/cyclonedx.go +++ b/pkg/report/cyclonedx/cyclonedx.go @@ -1,6 +1,7 @@ package cyclonedx import ( + "context" "io" cdx "github.com/CycloneDX/cyclonedx-go" @@ -26,8 +27,8 @@ func NewWriter(output io.Writer, appVersion string) Writer { } // Write writes the results in CycloneDX format -func (w Writer) Write(report types.Report) error { - bom, err := w.marshaler.Marshal(report) +func (w Writer) Write(ctx context.Context, report types.Report) error { + bom, err := w.marshaler.Marshal(ctx, report) if err != nil { return xerrors.Errorf("CycloneDX marshal error: %w", err) } diff --git a/pkg/report/github/github.go b/pkg/report/github/github.go index 8384f27b0e74..512a3dc9c528 100644 --- a/pkg/report/github/github.go +++ b/pkg/report/github/github.go @@ -1,6 +1,7 @@ package github import ( + "context" "encoding/json" "fmt" "io" @@ -71,11 +72,11 @@ type Writer struct { Version string } -func (w Writer) Write(report types.Report) error { +func (w Writer) Write(ctx context.Context, report types.Report) error { snapshot := &DependencySnapshot{} // use now() method that can be overwritten while integration tests run - snapshot.Scanned = clock.Now().Format(time.RFC3339) + snapshot.Scanned = clock.Now(ctx).Format(time.RFC3339) snapshot.Detector = Detector{ Name: "trivy", Version: w.Version, diff --git a/pkg/report/github/github_test.go b/pkg/report/github/github_test.go index b7187c9723c8..ada2e8c84880 100644 --- a/pkg/report/github/github_test.go +++ b/pkg/report/github/github_test.go @@ -2,6 +2,7 @@ package github_test import ( "bytes" + "context" "encoding/json" "testing" @@ -174,7 +175,7 @@ func TestWriter_Write(t *testing.T) { inputResults := tt.report - err := w.Write(inputResults) + err := w.Write(context.Background(), inputResults) assert.NoError(t, err) var got github.DependencySnapshot diff --git a/pkg/report/json.go b/pkg/report/json.go index 15da8041add0..57ac92cfe8ca 100644 --- a/pkg/report/json.go +++ b/pkg/report/json.go @@ -1,6 +1,7 @@ package report import ( + "context" "encoding/json" "fmt" "io" @@ -16,7 +17,7 @@ type JSONWriter struct { } // Write writes the results in JSON format -func (jw JSONWriter) Write(report types.Report) error { +func (jw JSONWriter) Write(ctx context.Context, report types.Report) error { output, err := json.MarshalIndent(report, "", " ") if err != nil { return xerrors.Errorf("failed to marshal json: %w", err) diff --git a/pkg/report/json_test.go b/pkg/report/json_test.go index ea486c145327..493fd6d8f641 100644 --- a/pkg/report/json_test.go +++ b/pkg/report/json_test.go @@ -2,6 +2,7 @@ package report_test import ( "bytes" + "context" "encoding/json" "testing" @@ -85,7 +86,7 @@ func TestReportWriter_JSON(t *testing.T) { }, } - err := jw.Write(inputResults) + err := jw.Write(context.Background(), inputResults) assert.NoError(t, err) var got types.Report diff --git a/pkg/report/predicate/vuln.go b/pkg/report/predicate/vuln.go index c1f15edb59de..9c975a4be76a 100644 --- a/pkg/report/predicate/vuln.go +++ b/pkg/report/predicate/vuln.go @@ -1,6 +1,7 @@ package predicate import ( + "context" "encoding/json" "fmt" "io" @@ -59,8 +60,7 @@ func NewVulnWriter(output io.Writer, version string) VulnWriter { } } -func (w VulnWriter) Write(report types.Report) error { - +func (w VulnWriter) Write(ctx context.Context, report types.Report) error { predicate := CosignVulnPredicate{} purl := packageurl.NewPackageURL("github", "aquasecurity", "trivy", w.version, nil, "") @@ -70,7 +70,7 @@ func (w VulnWriter) Write(report types.Report) error { Result: report, } - now := clock.Now() + now := clock.Now(ctx) predicate.Metadata = Metadata{ ScanStartedOn: now, ScanFinishedOn: now, diff --git a/pkg/report/predicate/vuln_test.go b/pkg/report/predicate/vuln_test.go index a09716b849c0..8477a3971bd4 100644 --- a/pkg/report/predicate/vuln_test.go +++ b/pkg/report/predicate/vuln_test.go @@ -2,6 +2,7 @@ package predicate_test import ( "bytes" + "context" "encoding/json" "testing" "time" @@ -84,7 +85,6 @@ func TestWriter_Write(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - inputResults := types.Report{ SchemaVersion: 2, ArtifactName: "alpine:3.14", @@ -98,10 +98,10 @@ func TestWriter_Write(t *testing.T) { output := bytes.NewBuffer(nil) - clock.SetFakeTime(t, time.Date(2022, 7, 22, 12, 20, 30, 5, time.UTC)) + ctx := clock.With(context.Background(), time.Date(2022, 7, 22, 12, 20, 30, 5, time.UTC)) writer := predicate.NewVulnWriter(output, "dev") - err := writer.Write(inputResults) + err := writer.Write(ctx, inputResults) require.NoError(t, err) var got predicate.CosignVulnPredicate diff --git a/pkg/report/sarif.go b/pkg/report/sarif.go index 621f6696b96f..800737a84258 100644 --- a/pkg/report/sarif.go +++ b/pkg/report/sarif.go @@ -1,6 +1,7 @@ package report import ( + "context" "fmt" "html" "io" @@ -121,7 +122,7 @@ func getRuleIndex(id string, indexes map[string]int) int { } } -func (sw *SarifWriter) Write(report types.Report) error { +func (sw *SarifWriter) Write(ctx context.Context, report types.Report) error { sarifReport, err := sarif.New(sarif.Version210) if err != nil { return xerrors.Errorf("error creating a new sarif template: %w", err) diff --git a/pkg/report/sarif_test.go b/pkg/report/sarif_test.go index 556491c1dcad..737662dcd536 100644 --- a/pkg/report/sarif_test.go +++ b/pkg/report/sarif_test.go @@ -549,7 +549,7 @@ func TestReportWriter_Sarif(t *testing.T) { w := report.SarifWriter{ Output: sarifWritten, } - err := w.Write(tt.input) + err := w.Write(nil, tt.input) assert.NoError(t, err) result := &sarif.Report{} diff --git a/pkg/report/spdx/spdx.go b/pkg/report/spdx/spdx.go index 848e26172dbe..7984db0e1517 100644 --- a/pkg/report/spdx/spdx.go +++ b/pkg/report/spdx/spdx.go @@ -1,6 +1,7 @@ package spdx import ( + "context" "encoding/json" "io" @@ -28,8 +29,8 @@ func NewWriter(output io.Writer, version string, spdxFormat types.Format) Writer } } -func (w Writer) Write(report types.Report) error { - spdxDoc, err := w.marshaler.Marshal(report) +func (w Writer) Write(ctx context.Context, report types.Report) error { + spdxDoc, err := w.marshaler.Marshal(ctx, report) if err != nil { return xerrors.Errorf("failed to marshal spdx: %w", err) } diff --git a/pkg/report/table/table.go b/pkg/report/table/table.go index 8be620f36589..652fda43a327 100644 --- a/pkg/report/table/table.go +++ b/pkg/report/table/table.go @@ -1,6 +1,7 @@ package table import ( + "context" "fmt" "io" "os" @@ -52,7 +53,7 @@ type Renderer interface { } // Write writes the result on standard output -func (tw Writer) Write(report types.Report) error { +func (tw Writer) Write(_ context.Context, report types.Report) error { for _, result := range report.Results { // Not display a table of custom resources if result.Class == types.ClassCustom { diff --git a/pkg/report/table/table_test.go b/pkg/report/table/table_test.go index cbce1fd18e22..90edcdabcb7b 100644 --- a/pkg/report/table/table_test.go +++ b/pkg/report/table/table_test.go @@ -354,7 +354,7 @@ package-lock.json dbTypes.SeverityMedium, }, } - err := writer.Write(types.Report{Results: tc.results}) + err := writer.Write(nil, types.Report{Results: tc.results}) assert.NoError(t, err) assert.Equal(t, tc.expectedOutput, tableWritten.String(), tc.name) }) diff --git a/pkg/report/template.go b/pkg/report/template.go index d320a420f722..7a28a65cfbb2 100644 --- a/pkg/report/template.go +++ b/pkg/report/template.go @@ -2,6 +2,7 @@ package report import ( "bytes" + "context" "encoding/xml" "html" "io" @@ -74,7 +75,7 @@ func NewTemplateWriter(output io.Writer, outputTemplate, appVersion string) (*Te } // Write writes result -func (tw TemplateWriter) Write(report types.Report) error { +func (tw TemplateWriter) Write(ctx context.Context, report types.Report) error { err := tw.Template.Execute(tw.Output, report.Results) if err != nil { return xerrors.Errorf("failed to write with template: %w", err) diff --git a/pkg/report/template_test.go b/pkg/report/template_test.go index 30db3595680e..4422e2c0e136 100644 --- a/pkg/report/template_test.go +++ b/pkg/report/template_test.go @@ -2,6 +2,7 @@ package report_test import ( "bytes" + "context" "os" "testing" "time" @@ -39,7 +40,8 @@ func TestReportWriter_Template(t *testing.T) { VulnerabilityID: "CVE-2019-0000", PkgName: "bar", Vulnerability: dbTypes.Vulnerability{ - Severity: dbTypes.SeverityHigh.String()}, + Severity: dbTypes.SeverityHigh.String(), + }, }, { VulnerabilityID: "CVE-2019-0001", @@ -164,7 +166,7 @@ func TestReportWriter_Template(t *testing.T) { } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - clock.SetFakeTime(t, time.Date(2020, 8, 10, 7, 28, 17, 958601, time.UTC)) + ctx := clock.With(context.Background(), time.Date(2020, 8, 10, 7, 28, 17, 958601, time.UTC)) os.Setenv("AWS_ACCOUNT_ID", "123456789012") got := bytes.Buffer{} @@ -180,7 +182,7 @@ func TestReportWriter_Template(t *testing.T) { w, err := report.NewTemplateWriter(&got, tc.template, "dev") require.NoError(t, err) - err = w.Write(inputReport) + err = w.Write(ctx, inputReport) assert.NoError(t, err) assert.Equal(t, tc.expected, got.String()) }) diff --git a/pkg/report/writer.go b/pkg/report/writer.go index c467f921d4d2..3a095bbbfc8e 100644 --- a/pkg/report/writer.go +++ b/pkg/report/writer.go @@ -39,7 +39,7 @@ func Write(ctx context.Context, report types.Report, option flag.Options) (err e // Compliance report if option.Compliance.Spec.ID != "" { - return complianceWrite(report, option, output) + return complianceWrite(ctx, report, option, output) } var writer Writer @@ -97,19 +97,19 @@ func Write(ctx context.Context, report types.Report, option flag.Options) (err e return xerrors.Errorf("unknown format: %v", option.Format) } - if err = writer.Write(report); err != nil { + if err = writer.Write(ctx, report); err != nil { return xerrors.Errorf("failed to write results: %w", err) } return nil } -func complianceWrite(report types.Report, opt flag.Options, output io.Writer) error { +func complianceWrite(ctx context.Context, report types.Report, opt flag.Options, output io.Writer) error { complianceReport, err := cr.BuildComplianceReport([]types.Results{report.Results}, opt.Compliance) if err != nil { return xerrors.Errorf("compliance report build error: %w", err) } - return cr.Write(complianceReport, cr.Option{ + return cr.Write(ctx, complianceReport, cr.Option{ Format: opt.Format, Report: opt.ReportFormat, Output: output, @@ -119,5 +119,5 @@ func complianceWrite(report types.Report, opt flag.Options, output io.Writer) er // Writer defines the result write operation type Writer interface { - Write(types.Report) error + Write(context.Context, types.Report) error } diff --git a/pkg/result/filter.go b/pkg/result/filter.go index 9dc738d318aa..524738043bb2 100644 --- a/pkg/result/filter.go +++ b/pkg/result/filter.go @@ -40,7 +40,7 @@ func Filter(ctx context.Context, report types.Report, opt FilterOption) error { return xerrors.Errorf("VEX error: %w", err) } - ignoreConf, err := getIgnoredFindings(opt.IgnoreFile) + ignoreConf, err := getIgnoredFindings(ctx, opt.IgnoreFile) if err != nil { return xerrors.Errorf("%s error: %w", opt.IgnoreFile, err) } diff --git a/pkg/result/filter_test.go b/pkg/result/filter_test.go index 7392a4779861..db9770d886d1 100644 --- a/pkg/result/filter_test.go +++ b/pkg/result/filter_test.go @@ -1072,9 +1072,9 @@ func TestFilter(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { fakeTime := time.Date(2020, 8, 10, 7, 28, 17, 958601, time.UTC) - clock.SetFakeTime(t, fakeTime) + ctx := clock.With(context.Background(), fakeTime) - err := result.Filter(context.Background(), tt.args.report, result.FilterOption{ + err := result.Filter(ctx, tt.args.report, result.FilterOption{ Severities: tt.args.severities, VEXPath: tt.args.vexPath, IgnoreStatuses: tt.args.ignoreStatuses, diff --git a/pkg/result/ignore.go b/pkg/result/ignore.go index 9a2e1c3156a2..ee7fef0d6ce7 100644 --- a/pkg/result/ignore.go +++ b/pkg/result/ignore.go @@ -2,6 +2,7 @@ package result import ( "bufio" + "context" "errors" "io/fs" "os" @@ -72,11 +73,11 @@ func pathMatch(path string, patterns []string) bool { return false } -func (f *IgnoreFindings) Filter() { +func (f *IgnoreFindings) Filter(ctx context.Context) { var findings IgnoreFindings for _, finding := range *f { // Filter out expired ignore findings - if !finding.ExpiredAt.IsZero() && finding.ExpiredAt.Before(clock.Now()) { + if !finding.ExpiredAt.IsZero() && finding.ExpiredAt.Before(clock.Now(ctx)) { continue } @@ -101,7 +102,7 @@ type IgnoreConfig struct { Licenses IgnoreFindings `yaml:"licenses"` } -func getIgnoredFindings(ignoreFile string) (IgnoreConfig, error) { +func getIgnoredFindings(ctx context.Context, ignoreFile string) (IgnoreConfig, error) { var conf IgnoreConfig if _, err := os.Stat(ignoreFile); errors.Is(err, fs.ErrNotExist) { // .trivyignore doesn't necessarily exist @@ -127,10 +128,10 @@ func getIgnoredFindings(ignoreFile string) (IgnoreConfig, error) { } } - conf.Vulnerabilities.Filter() - conf.Misconfigurations.Filter() - conf.Secrets.Filter() - conf.Licenses.Filter() + conf.Vulnerabilities.Filter(ctx) + conf.Misconfigurations.Filter(ctx) + conf.Secrets.Filter(ctx) + conf.Licenses.Filter(ctx) return conf, nil } diff --git a/pkg/rpc/server/listen.go b/pkg/rpc/server/listen.go index 7a6d4c46eeb6..33a3a8ee8a81 100644 --- a/pkg/rpc/server/listen.go +++ b/pkg/rpc/server/listen.go @@ -53,13 +53,12 @@ func NewServer(appVersion, addr, cacheDir, token, tokenHeader, dbRepository stri } // ListenAndServe starts Trivy server -func (s Server) ListenAndServe(serverCache cache.Cache, skipDBUpdate bool) error { +func (s Server) ListenAndServe(ctx context.Context, serverCache cache.Cache, skipDBUpdate bool) error { requestWg := &sync.WaitGroup{} dbUpdateWg := &sync.WaitGroup{} go func() { worker := newDBWorker(dbc.NewClient(s.cacheDir, true, dbc.WithDBRepository(s.dbRepository))) - ctx := context.Background() for { time.Sleep(updateInterval) if err := worker.update(ctx, s.appVersion, s.cacheDir, skipDBUpdate, dbUpdateWg, requestWg, s.RegistryOptions); err != nil { @@ -68,13 +67,13 @@ func (s Server) ListenAndServe(serverCache cache.Cache, skipDBUpdate bool) error } }() - mux := newServeMux(serverCache, dbUpdateWg, requestWg, s.token, s.tokenHeader, s.cacheDir) + mux := newServeMux(ctx, serverCache, dbUpdateWg, requestWg, s.token, s.tokenHeader, s.cacheDir) log.Logger.Infof("Listening %s...", s.addr) return http.ListenAndServe(s.addr, mux) } -func newServeMux(serverCache cache.Cache, dbUpdateWg, requestWg *sync.WaitGroup, +func newServeMux(ctx context.Context, serverCache cache.Cache, dbUpdateWg, requestWg *sync.WaitGroup, token, tokenHeader, cacheDir string) *http.ServeMux { withWaitGroup := func(base http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -85,7 +84,7 @@ func newServeMux(serverCache cache.Cache, dbUpdateWg, requestWg *sync.WaitGroup, requestWg.Add(1) defer requestWg.Done() - base.ServeHTTP(w, r) + base.ServeHTTP(w, r.WithContext(ctx)) }) } diff --git a/pkg/rpc/server/listen_test.go b/pkg/rpc/server/listen_test.go index 834627b92762..5fff3f3bc46e 100644 --- a/pkg/rpc/server/listen_test.go +++ b/pkg/rpc/server/listen_test.go @@ -253,8 +253,8 @@ func Test_newServeMux(t *testing.T) { require.NoError(t, err) defer func() { _ = c.Close() }() - ts := httptest.NewServer(newServeMux( - c, dbUpdateWg, requestWg, tt.args.token, tt.args.tokenHeader, ""), + ts := httptest.NewServer(newServeMux(context.Background(), c, dbUpdateWg, requestWg, tt.args.token, + tt.args.tokenHeader, ""), ) defer ts.Close() @@ -284,8 +284,8 @@ func Test_VersionEndpoint(t *testing.T) { require.NoError(t, err) defer func() { _ = c.Close() }() - ts := httptest.NewServer(newServeMux( - c, dbUpdateWg, requestWg, "", "", "testdata/testcache"), + ts := httptest.NewServer(newServeMux(context.Background(), c, dbUpdateWg, requestWg, "", "", + "testdata/testcache"), ) defer ts.Close() diff --git a/pkg/sbom/cyclonedx/core/cyclonedx.go b/pkg/sbom/cyclonedx/core/cyclonedx.go index 5d61f2993f72..2c5945b35416 100644 --- a/pkg/sbom/cyclonedx/core/cyclonedx.go +++ b/pkg/sbom/cyclonedx/core/cyclonedx.go @@ -1,6 +1,7 @@ package core import ( + "context" "fmt" "sort" "strconv" @@ -60,10 +61,10 @@ func NewCycloneDX(version string) *CycloneDX { } } -func (c *CycloneDX) Marshal(root *Component) *cdx.BOM { +func (c *CycloneDX) Marshal(ctx context.Context, root *Component) *cdx.BOM { bom := cdx.NewBOM() bom.SerialNumber = uuid.New().URN() - bom.Metadata = c.Metadata() + bom.Metadata = c.Metadata(ctx) components := make(map[string]*cdx.Component) dependencies := make(map[string]*[]string) @@ -180,9 +181,9 @@ func (c *CycloneDX) BOMRef(component *Component) string { return component.PackageURL.BOMRef() } -func (c *CycloneDX) Metadata() *cdx.Metadata { +func (c *CycloneDX) Metadata(ctx context.Context) *cdx.Metadata { return &cdx.Metadata{ - Timestamp: clock.Now().UTC().Format(timeLayout), + Timestamp: clock.Now(ctx).UTC().Format(timeLayout), Tools: &[]cdx.Tool{ { Vendor: ToolVendor, diff --git a/pkg/sbom/cyclonedx/core/cyclonedx_test.go b/pkg/sbom/cyclonedx/core/cyclonedx_test.go index 2685a56ef733..d3b37308140f 100644 --- a/pkg/sbom/cyclonedx/core/cyclonedx_test.go +++ b/pkg/sbom/cyclonedx/core/cyclonedx_test.go @@ -1,6 +1,7 @@ package core_test import ( + "context" "testing" "time" @@ -365,11 +366,11 @@ func TestMarshaler_CoreComponent(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - clock.SetFakeTime(t, time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC)) + 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 := core.NewCycloneDX("dev") - got := marshaler.Marshal(tt.rootComponent) + got := marshaler.Marshal(ctx, tt.rootComponent) assert.Equal(t, tt.want, got) }) } diff --git a/pkg/sbom/cyclonedx/marshal.go b/pkg/sbom/cyclonedx/marshal.go index 586b82d3ee39..16cb37c2d2fc 100644 --- a/pkg/sbom/cyclonedx/marshal.go +++ b/pkg/sbom/cyclonedx/marshal.go @@ -1,6 +1,7 @@ package cyclonedx import ( + "context" "fmt" "strconv" "strings" @@ -57,14 +58,14 @@ func NewMarshaler(version string) *Marshaler { } // Marshal converts the Trivy report to the CycloneDX format -func (e *Marshaler) Marshal(report types.Report) (*cdx.BOM, error) { +func (e *Marshaler) Marshal(ctx context.Context, report types.Report) (*cdx.BOM, error) { // Convert root, err := e.MarshalReport(report) if err != nil { return nil, xerrors.Errorf("failed to marshal report: %w", err) } - return e.core.Marshal(root), nil + return e.core.Marshal(ctx, root), nil } func (e *Marshaler) MarshalReport(r types.Report) (*core.Component, error) { diff --git a/pkg/sbom/cyclonedx/marshal_test.go b/pkg/sbom/cyclonedx/marshal_test.go index 234f2b9d0ae5..8169baf040bb 100644 --- a/pkg/sbom/cyclonedx/marshal_test.go +++ b/pkg/sbom/cyclonedx/marshal_test.go @@ -1,6 +1,7 @@ package cyclonedx_test import ( + "context" "github.com/package-url/packageurl-go" "testing" "time" @@ -1825,11 +1826,11 @@ func TestMarshaler_Marshal(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - clock.SetFakeTime(t, time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC)) + 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 := cyclonedx.NewMarshaler("dev") - got, err := marshaler.Marshal(tt.inputReport) + got, err := marshaler.Marshal(ctx, tt.inputReport) require.NoError(t, err) assert.Equal(t, tt.want, got) }) diff --git a/pkg/sbom/spdx/marshal.go b/pkg/sbom/spdx/marshal.go index 642e7d7629ab..b88f92c1c23f 100644 --- a/pkg/sbom/spdx/marshal.go +++ b/pkg/sbom/spdx/marshal.go @@ -1,6 +1,7 @@ package spdx import ( + "context" "fmt" "sort" "strconv" @@ -106,7 +107,7 @@ func NewMarshaler(version string, opts ...marshalOption) *Marshaler { return m } -func (m *Marshaler) Marshal(r types.Report) (*spdx.Document, error) { +func (m *Marshaler) Marshal(ctx context.Context, r types.Report) (*spdx.Document, error) { var relationShips []*spdx.Relationship packages := make(map[spdx.ElementID]*spdx.Package) pkgDownloadLocation := getPackageDownloadLocation(r.ArtifactType, r.ArtifactName) @@ -186,7 +187,7 @@ func (m *Marshaler) Marshal(r types.Report) (*spdx.Document, error) { CreatorType: "Tool", }, }, - Created: clock.Now().UTC().Format(time.RFC3339), + Created: clock.Now(ctx).UTC().Format(time.RFC3339), }, Packages: toPackages(packages), Relationships: relationShips, diff --git a/pkg/sbom/spdx/marshal_test.go b/pkg/sbom/spdx/marshal_test.go index eb73274bbc30..abd356d6ec7e 100644 --- a/pkg/sbom/spdx/marshal_test.go +++ b/pkg/sbom/spdx/marshal_test.go @@ -1,6 +1,7 @@ package spdx_test import ( + "context" "github.com/package-url/packageurl-go" "hash/fnv" "testing" @@ -1105,11 +1106,11 @@ func TestMarshaler_Marshal(t *testing.T) { return h.Sum64(), nil } - clock.SetFakeTime(t, time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC)) + 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)) - spdxDoc, err := marshaler.Marshal(tc.inputReport) + spdxDoc, err := marshaler.Marshal(ctx, tc.inputReport) require.NoError(t, err) assert.Equal(t, tc.wantSBOM, spdxDoc) diff --git a/pkg/scanner/local/scan.go b/pkg/scanner/local/scan.go index edb39b15ae8f..f76c5c7ab0ea 100644 --- a/pkg/scanner/local/scan.go +++ b/pkg/scanner/local/scan.go @@ -120,7 +120,7 @@ func (s Scanner) ScanTarget(ctx context.Context, target types.ScanTarget, option // Scan packages for vulnerabilities if options.Scanners.Enabled(types.VulnerabilityScanner) { var vulnResults types.Results - vulnResults, eosl, err = s.scanVulnerabilities(target, options) + vulnResults, eosl, err = s.scanVulnerabilities(ctx, target, options) if err != nil { return nil, ftypes.OS{}, xerrors.Errorf("failed to detect vulnerabilities: %w", err) } @@ -166,13 +166,13 @@ func (s Scanner) ScanTarget(ctx context.Context, target types.ScanTarget, option return results, target.OS, nil } -func (s Scanner) scanVulnerabilities(target types.ScanTarget, options types.ScanOptions) ( +func (s Scanner) scanVulnerabilities(ctx context.Context, target types.ScanTarget, options types.ScanOptions) ( types.Results, bool, error) { var eosl bool var results types.Results if slices.Contains(options.VulnType, types.VulnTypeOS) { - vuln, detectedEOSL, err := s.osPkgScanner.Scan(target, options) + vuln, detectedEOSL, err := s.osPkgScanner.Scan(ctx, target, options) if err != nil { return nil, false, xerrors.Errorf("unable to scan OS packages: %w", err) } else if vuln.Target != "" { diff --git a/pkg/scanner/ospkg/scan.go b/pkg/scanner/ospkg/scan.go index a07d6e6b2091..ebc94b1dab9c 100644 --- a/pkg/scanner/ospkg/scan.go +++ b/pkg/scanner/ospkg/scan.go @@ -1,6 +1,8 @@ package ospkg import ( + "context" + "errors" "fmt" "sort" "time" @@ -14,7 +16,7 @@ import ( type Scanner interface { Packages(target types.ScanTarget, options types.ScanOptions) types.Result - Scan(target types.ScanTarget, options types.ScanOptions) (types.Result, bool, error) + Scan(ctx context.Context, target types.ScanTarget, options types.ScanOptions) (types.Result, bool, error) } type scanner struct{} @@ -37,7 +39,7 @@ func (s *scanner) Packages(target types.ScanTarget, _ types.ScanOptions) types.R } } -func (s *scanner) Scan(target types.ScanTarget, _ types.ScanOptions) (types.Result, bool, error) { +func (s *scanner) Scan(ctx context.Context, target types.ScanTarget, _ types.ScanOptions) (types.Result, bool, error) { if !target.OS.Detected() { log.Logger.Debug("Detected OS: unknown") return types.Result{}, false, nil @@ -49,9 +51,9 @@ func (s *scanner) Scan(target types.ScanTarget, _ types.ScanOptions) (types.Resu target.OS.Name += "-ESM" } - vulns, eosl, err := ospkgDetector.Detect("", target.OS.Family, target.OS.Name, target.Repository, time.Time{}, + vulns, eosl, err := ospkgDetector.Detect(ctx, "", target.OS.Family, target.OS.Name, target.Repository, time.Time{}, target.Packages) - if err == ospkgDetector.ErrUnsupportedOS { + if errors.Is(err, ospkgDetector.ErrUnsupportedOS) { return types.Result{}, false, nil } else if err != nil { return types.Result{}, false, xerrors.Errorf("failed vulnerability detection of OS packages: %w", err) diff --git a/pkg/scanner/scan.go b/pkg/scanner/scan.go index cc1950b31fc9..82e900024acb 100644 --- a/pkg/scanner/scan.go +++ b/pkg/scanner/scan.go @@ -173,7 +173,7 @@ func (s Scanner) ScanArtifact(ctx context.Context, options types.ScanOptions) (t return types.Report{ SchemaVersion: report.SchemaVersion, - CreatedAt: clock.Now(), + CreatedAt: clock.Now(ctx), ArtifactName: artifactInfo.Name, ArtifactType: artifactInfo.Type, Metadata: types.Metadata{ diff --git a/pkg/scanner/scan_test.go b/pkg/scanner/scan_test.go index ba78193e7962..78606299303b 100644 --- a/pkg/scanner/scan_test.go +++ b/pkg/scanner/scan_test.go @@ -3,13 +3,13 @@ package scanner import ( "context" "errors" + "github.com/aquasecurity/trivy/pkg/clock" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/aquasecurity/trivy/pkg/clock" "github.com/aquasecurity/trivy/pkg/fanal/artifact" ftypes "github.com/aquasecurity/trivy/pkg/fanal/types" "github.com/aquasecurity/trivy/pkg/types" @@ -19,7 +19,6 @@ func TestScanner_ScanArtifact(t *testing.T) { type args struct { options types.ScanOptions } - clock.SetFakeTime(t, time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC)) tests := []struct { name string args args @@ -192,6 +191,7 @@ func TestScanner_ScanArtifact(t *testing.T) { }, } for _, tt := range tests { + ctx := clock.With(context.Background(), time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC)) t.Run(tt.name, func(t *testing.T) { d := new(MockDriver) d.ApplyScanExpectation(tt.scanExpectation) @@ -200,7 +200,7 @@ func TestScanner_ScanArtifact(t *testing.T) { mockArtifact.ApplyInspectExpectation(tt.inspectExpectation) s := NewScanner(d, mockArtifact) - got, err := s.ScanArtifact(context.Background(), tt.args.options) + got, err := s.ScanArtifact(ctx, tt.args.options) if tt.wantErr != "" { require.NotNil(t, err, tt.name) require.Contains(t, err.Error(), tt.wantErr, tt.name) From 7f2e4223ff87e05a475d19e952ed9a888a6b4eee Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 11:43:57 +0000 Subject: [PATCH 2/5] chore(deps): bump google.golang.org/protobuf from 1.31.0 to 1.32.0 (#5855) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 550f357eeb93..25beafb0a2d1 100644 --- a/go.mod +++ b/go.mod @@ -109,7 +109,7 @@ require ( golang.org/x/term v0.15.0 golang.org/x/text v0.14.0 golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 - google.golang.org/protobuf v1.31.0 + google.golang.org/protobuf v1.32.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.28.4 k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 diff --git a/go.sum b/go.sum index a9840929c6d0..6968a572969f 100644 --- a/go.sum +++ b/go.sum @@ -2467,8 +2467,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.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= 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= From b1489f3485fd004df99c01fe82df0ef2726a9967 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 11:53:52 +0000 Subject: [PATCH 3/5] chore(deps): bump github.com/hashicorp/go-getter from 1.7.2 to 1.7.3 (#5856) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 25beafb0a2d1..fd6f4bf88ff2 100644 --- a/go.mod +++ b/go.mod @@ -54,7 +54,7 @@ require ( github.com/google/licenseclassifier/v2 v2.0.0 github.com/google/uuid v1.4.0 github.com/google/wire v0.5.0 - github.com/hashicorp/go-getter v1.7.2 + github.com/hashicorp/go-getter v1.7.3 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/golang-lru/v2 v2.0.6 github.com/in-toto/in-toto-golang v0.9.0 diff --git a/go.sum b/go.sum index 6968a572969f..48c44a683ea9 100644 --- a/go.sum +++ b/go.sum @@ -1138,8 +1138,8 @@ github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-getter v1.7.2 h1:uJDtyXwEfalmp1PqdxuhZqrNkUyClZAhVeZYTArbqkg= -github.com/hashicorp/go-getter v1.7.2/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= +github.com/hashicorp/go-getter v1.7.3 h1:bN2+Fw9XPFvOCjB0UOevFIMICZ7G2XSQHzfvLUyOM5E= +github.com/hashicorp/go-getter v1.7.3/go.mod h1:W7TalhMmbPmsSMdNjD0ZskARur/9GJ17cfHTRtXV744= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= From 013df4c6b8f760adc8bf8a42d5d4623ed2ee5c2a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 Jan 2024 09:12:39 +0400 Subject: [PATCH 4/5] chore(deps): bump github.com/samber/lo from 1.38.1 to 1.39.0 (#5850) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fd6f4bf88ff2..0795f0a13e29 100644 --- a/go.mod +++ b/go.mod @@ -83,7 +83,7 @@ require ( github.com/owenrumney/go-sarif/v2 v2.3.0 github.com/package-url/packageurl-go v0.1.2 github.com/quasilyte/go-ruleguard/dsl v0.3.22 - github.com/samber/lo v1.38.1 + github.com/samber/lo v1.39.0 github.com/saracen/walker v0.1.3 github.com/secure-systems-lab/go-securesystemslib v0.7.0 github.com/sigstore/rekor v1.2.2 diff --git a/go.sum b/go.sum index 48c44a683ea9..b9809daebb5e 100644 --- a/go.sum +++ b/go.sum @@ -1576,8 +1576,8 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= -github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= +github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/saracen/walker v0.1.3 h1:YtcKKmpRPy6XJTHJ75J2QYXXZYWnZNQxPCVqZSHVV/g= github.com/saracen/walker v0.1.3/go.mod h1:FU+7qU8DeQQgSZDmmThMJi93kPkLFgy0oVAcLxurjIk= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= From 30eff9c83e4c06eaadb5c1f09d89d08c01014d79 Mon Sep 17 00:00:00 2001 From: DmitriyLewen <91113035+DmitriyLewen@users.noreply.github.com> Date: Thu, 4 Jan 2024 11:16:35 +0600 Subject: [PATCH 5/5] feat(nodejs): add yarn alias support (#5818) Signed-off-by: knqyf263 Co-authored-by: knqyf263 --- docs/docs/coverage/language/nodejs.md | 5 +- .../nodejs/yarn/testdata/alias/package.json | 14 +++ .../nodejs/yarn/testdata/alias/yarn.lock | 44 +++++++ .../analyzer/language/nodejs/yarn/yarn.go | 38 ++++-- .../language/nodejs/yarn/yarn_test.go | 111 ++++++++++++++++++ 5 files changed, 201 insertions(+), 11 deletions(-) create mode 100644 pkg/fanal/analyzer/language/nodejs/yarn/testdata/alias/package.json create mode 100644 pkg/fanal/analyzer/language/nodejs/yarn/testdata/alias/yarn.lock diff --git a/docs/docs/coverage/language/nodejs.md b/docs/docs/coverage/language/nodejs.md index 1489c459cd7d..393491b34f46 100644 --- a/docs/docs/coverage/language/nodejs.md +++ b/docs/docs/coverage/language/nodejs.md @@ -42,7 +42,10 @@ By default, Trivy doesn't report development dependencies. Use the `--include-de ### Yarn Trivy parses `yarn.lock`, which doesn't contain information about development dependencies. -To exclude devDependencies, `package.json` also needs to be present next to `yarn.lock`. +Trivy also uses `package.json` file to handle [aliases](https://classic.yarnpkg.com/lang/en/docs/cli/add/#toc-yarn-add-alias). + +To exclude devDependencies and allow aliases, `package.json` also needs to be present next to `yarn.lock`. + Trivy analyzes `.yarn` (Yarn 2+) or `node_modules` (Yarn Classic) folder next to the yarn.lock file to detect licenses. By default, Trivy doesn't report development dependencies. Use the `--include-dev-deps` flag to include them. diff --git a/pkg/fanal/analyzer/language/nodejs/yarn/testdata/alias/package.json b/pkg/fanal/analyzer/language/nodejs/yarn/testdata/alias/package.json new file mode 100644 index 000000000000..caa6e0f3f637 --- /dev/null +++ b/pkg/fanal/analyzer/language/nodejs/yarn/testdata/alias/package.json @@ -0,0 +1,14 @@ +{ + "name": "test", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "devDependencies": { + "foo-json": "npm:@types/jsonstream@0.8.33", + "foo-uuid": "npm:@types/uuid" + }, + "dependencies": { + "foo-debug": "npm:debug@^4.3", + "foo-ms": "npm:ms" + } +} diff --git a/pkg/fanal/analyzer/language/nodejs/yarn/testdata/alias/yarn.lock b/pkg/fanal/analyzer/language/nodejs/yarn/testdata/alias/yarn.lock new file mode 100644 index 000000000000..cb68abb657f9 --- /dev/null +++ b/pkg/fanal/analyzer/language/nodejs/yarn/testdata/alias/yarn.lock @@ -0,0 +1,44 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/node@*": + version "20.10.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.5.tgz#47ad460b514096b7ed63a1dae26fad0914ed3ab2" + integrity sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw== + dependencies: + undici-types "~5.26.4" + +"foo-debug@npm:debug@^4.3": + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +"foo-json@npm:@types/jsonstream@0.8.33": + version "0.8.33" + resolved "https://registry.yarnpkg.com/@types/jsonstream/-/jsonstream-0.8.33.tgz#7d37a16a78cf68a67858110dc1767023436fca23" + integrity sha512-yhg1SNOgJ8y2nOkvAQ1zZ1Z2xibxgFs7984+EeBPuWgo/TbuYo79+rj2wUVch3KF4GhhcwAi/AlJcehmLCXb3g== + dependencies: + "@types/node" "*" + +"foo-ms@npm:ms": + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +"foo-uuid@npm:@types/uuid": + version "9.0.7" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.7.tgz#b14cebc75455eeeb160d5fe23c2fcc0c64f724d8" + integrity sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== diff --git a/pkg/fanal/analyzer/language/nodejs/yarn/yarn.go b/pkg/fanal/analyzer/language/nodejs/yarn/yarn.go index d42c28e47128..3089b881e2e3 100644 --- a/pkg/fanal/analyzer/language/nodejs/yarn/yarn.go +++ b/pkg/fanal/analyzer/language/nodejs/yarn/yarn.go @@ -9,6 +9,7 @@ import ( "os" "path" "path/filepath" + "regexp" "sort" "strings" @@ -36,6 +37,10 @@ func init() { const version = 2 +// Taken from Yarn +// cf. https://github.com/yarnpkg/yarn/blob/328fd596de935acc6c3e134741748fcc62ec3739/src/resolvers/exotics/registry-resolver.js#L12 +var fragmentRegexp = regexp.MustCompile(`(\S+):(@?.*?)(@(.*?)|)$`) + type yarnAnalyzer struct { packageJsonParser *packagejson.Parser lockParser godeptypes.Parser @@ -193,17 +198,30 @@ func (a yarnAnalyzer) walkDependencies(libs []types.Package, pkgIDs map[string]t // Identify direct dependencies pkgs := make(map[string]types.Package) for _, pkg := range libs { - if constraint, ok := directDeps[pkg.Name]; ok { - // npm has own comparer to compare versions - if match, err := a.comparer.MatchVersion(pkg.Version, constraint); err != nil { - return nil, xerrors.Errorf("unable to match version for %s", pkg.Name) - } else if match { - // Mark as a direct dependency - pkg.Indirect = false - pkg.Dev = dev - pkgs[pkg.ID] = pkg - } + constraint, ok := directDeps[pkg.Name] + if !ok { + continue + } + + // Handle aliases + // cf. https://classic.yarnpkg.com/lang/en/docs/cli/add/#toc-yarn-add-alias + if m := fragmentRegexp.FindStringSubmatch(constraint); len(m) == 5 { + pkg.Name = m[2] // original name + constraint = m[4] } + + // npm has own comparer to compare versions + if match, err := a.comparer.MatchVersion(pkg.Version, constraint); err != nil { + return nil, xerrors.Errorf("unable to match version for %s", pkg.Name) + } else if !match { + continue + } + + // Mark as a direct dependency + pkg.Indirect = false + pkg.Dev = dev + pkgs[pkg.ID] = pkg + } // Walk indirect dependencies diff --git a/pkg/fanal/analyzer/language/nodejs/yarn/yarn_test.go b/pkg/fanal/analyzer/language/nodejs/yarn/yarn_test.go index eb8be228afd5..081d06c5954f 100644 --- a/pkg/fanal/analyzer/language/nodejs/yarn/yarn_test.go +++ b/pkg/fanal/analyzer/language/nodejs/yarn/yarn_test.go @@ -318,6 +318,117 @@ func Test_yarnLibraryAnalyzer_Analyze(t *testing.T) { }, }, }, + { + name: "happy path with alias rewrite", + dir: "testdata/alias", + want: &analyzer.AnalysisResult{ + Applications: []types.Application{ + { + Type: types.Yarn, + FilePath: "yarn.lock", + Libraries: types.Packages{ + { + ID: "foo-json@0.8.33", + Name: "@types/jsonstream", + Version: "0.8.33", + Indirect: false, + Dev: true, + Locations: []types.Location{ + { + StartLine: 19, + EndLine: 24, + }, + }, + DependsOn: []string{ + "@types/node@20.10.5", + }, + }, + { + ID: "@types/node@20.10.5", + Name: "@types/node", + Version: "20.10.5", + Indirect: true, + Dev: true, + Locations: []types.Location{ + { + StartLine: 5, + EndLine: 10, + }, + }, + DependsOn: []string{ + "undici-types@5.26.5", + }, + }, + { + ID: "foo-uuid@9.0.7", + Name: "@types/uuid", + Version: "9.0.7", + Indirect: false, + Dev: true, + Locations: []types.Location{ + { + StartLine: 31, + EndLine: 34, + }, + }, + }, + { + ID: "foo-debug@4.3.4", + Name: "debug", + Version: "4.3.4", + Indirect: false, + Locations: []types.Location{ + { + StartLine: 12, + EndLine: 17, + }, + }, + DependsOn: []string{ + "ms@2.1.2", + }, + }, + { + ID: "ms@2.1.2", + Name: "ms", + Version: "2.1.2", + Indirect: true, + Locations: []types.Location{ + { + StartLine: 36, + EndLine: 39, + }, + }, + }, + { + ID: "foo-ms@2.1.3", + Name: "ms", + Version: "2.1.3", + Indirect: false, + Locations: []types.Location{ + { + StartLine: 26, + EndLine: 29, + }, + }, + }, + { + ID: "undici-types@5.26.5", + Name: "undici-types", + Version: "5.26.5", + Indirect: true, + Dev: true, + Locations: []types.Location{ + { + StartLine: 41, + EndLine: 44, + }, + }, + }, + }, + }, + }, + }, + }, { name: "monorepo", dir: "testdata/monorepo",