Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): Add available version checking #8553

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ trivy filesystem [flags] PATH
--list-all-pkgs output all packages in the JSON report regardless of vulnerability
--misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot])
--module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules")
--no-notices suppress notices about version updates and Trivy announcements
--no-progress suppress progress bar
--offline-scan do not issue API requests to identify dependencies
-o, --output string output file name
Expand Down
1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_image.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ trivy image [flags] IMAGE_NAME
--max-image-size string [EXPERIMENTAL] maximum image size to process, specified in a human-readable format (e.g., '44kB', '17MB'); an error will be returned if the image exceeds this size
--misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot])
--module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules")
--no-notices suppress notices about version updates and Trivy announcements
--no-progress suppress progress bar
--offline-scan do not issue API requests to identify dependencies
-o, --output string output file name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ trivy kubernetes [flags] [CONTEXT]
--kubeconfig string specify the kubeconfig file path to use
--list-all-pkgs output all packages in the JSON report regardless of vulnerability
--misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot])
--no-notices suppress notices about version updates and Trivy announcements
--no-progress suppress progress bar
--node-collector-imageref string indicate the image reference for the node-collector scan job (default "ghcr.io/aquasecurity/node-collector:0.3.1")
--node-collector-namespace string specify the namespace in which the node-collector job should be deployed (default "trivy-temp")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL)
--list-all-pkgs output all packages in the JSON report regardless of vulnerability
--misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot])
--module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules")
--no-notices suppress notices about version updates and Trivy announcements
--no-progress suppress progress bar
--offline-scan do not issue API requests to identify dependencies
-o, --output string output file name
Expand Down
1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_rootfs.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ trivy rootfs [flags] ROOTDIR
--list-all-pkgs output all packages in the JSON report regardless of vulnerability
--misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot])
--module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules")
--no-notices suppress notices about version updates and Trivy announcements
--no-progress suppress progress bar
--offline-scan do not issue API requests to identify dependencies
-o, --output string output file name
Expand Down
1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_sbom.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ trivy sbom [flags] SBOM_PATH
--ignorefile string specify .trivyignore file (default ".trivyignore")
--java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [mirror.gcr.io/aquasec/trivy-java-db:1,ghcr.io/aquasecurity/trivy-java-db:1])
--list-all-pkgs output all packages in the JSON report regardless of vulnerability
--no-notices suppress notices about version updates and Trivy announcements
--no-progress suppress progress bar
--offline-scan do not issue API requests to identify dependencies
-o, --output string output file name
Expand Down
1 change: 1 addition & 0 deletions docs/docs/references/configuration/cli/trivy_vm.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ trivy vm [flags] VM_IMAGE
--list-all-pkgs output all packages in the JSON report regardless of vulnerability
--misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot])
--module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules")
--no-notices suppress notices about version updates and Trivy announcements
--no-progress suppress progress bar
--offline-scan do not issue API requests to identify dependencies
-o, --output string output file name
Expand Down
3 changes: 3 additions & 0 deletions docs/docs/references/configuration/config-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,9 @@ repository:
## Scan options

```yaml
# Same as '--no-notices'
no-notices: false

scan:
# Same as '--detection-priority'
detection-priority: "precise"
Expand Down
6 changes: 6 additions & 0 deletions pkg/commands/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
k8scommands "github.com/aquasecurity/trivy/pkg/k8s/commands"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/module"
"github.com/aquasecurity/trivy/pkg/notification"
"github.com/aquasecurity/trivy/pkg/plugin"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/version"
Expand Down Expand Up @@ -234,6 +235,11 @@ func NewRootCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
return cmd.Help()
}
},
PersistentPostRun: func(_ *cobra.Command, _ []string) {
// if the check hasn't run then it will
// return immediately and won't print anything
notification.PrintNotices(os.Stderr)
},
}

// Add version format flag, only json is supported
Expand Down
9 changes: 9 additions & 0 deletions pkg/commands/artifact/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ import (
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/misconf"
"github.com/aquasecurity/trivy/pkg/module"
"github.com/aquasecurity/trivy/pkg/notification"
"github.com/aquasecurity/trivy/pkg/policy"
pkgReport "github.com/aquasecurity/trivy/pkg/report"
"github.com/aquasecurity/trivy/pkg/result"
"github.com/aquasecurity/trivy/pkg/rpc/client"
"github.com/aquasecurity/trivy/pkg/scan"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/version/app"
"github.com/aquasecurity/trivy/pkg/version/doc"
)

Expand Down Expand Up @@ -136,6 +138,13 @@ func NewRunner(ctx context.Context, cliOptions flag.Options, opts ...RunnerOptio
m.Register()
r.module = m

// Make a silent attempt to check for updates in the background
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DmitriyLewen - I'm not sure if this is the optimal place to hook into Scan runs to trigger the best efforts check for notices. Happy to move if somewhere more appropriate can be suggested

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think your previous place was better.
You run the check before rootCommand (PersistentPreRunE) and show notifications after rootCommand (PersistentPostRun).

Both commands are on the same level

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to sasify you suggestion that it only run on scan commands. If its in the root command there will need to be some inspection of the called sub command to ensure its a scan.

As it's a best efforts request, I think I should go back to it being run as part of the preRun and all commands get it so --no-notices goes back to being a global flag. wdyt?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked at the current implementation again. Let's leave it like this. Given that we only need notifications when scanning, this would be the optimal place.
Thanks for clarifying this nuance.

// only do this if the user has not disabled notices or is running
// in quiet mode
if !cliOptions.Quiet && !cliOptions.NoNotices {
notification.CheckForNotices(ctx, app.Version(), os.Args[1:])
}

return r, nil
}

Expand Down
10 changes: 10 additions & 0 deletions pkg/flag/scan_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ var (
ConfigName: "scan.distro",
Usage: "[EXPERIMENTAL] specify a distribution, <family>/<version>",
}
NoNoticesFlag = Flag[bool]{
Name: "no-notices",
ConfigName: "no-notices",
Usage: "suppress notices about version updates and Trivy announcements",
}
)

type ScanFlagGroup struct {
Expand All @@ -132,6 +137,7 @@ type ScanFlagGroup struct {
RekorURL *Flag[string]
DetectionPriority *Flag[string]
DistroFlag *Flag[string]
NoNotices *Flag[bool]
}

type ScanOptions struct {
Expand All @@ -146,6 +152,7 @@ type ScanOptions struct {
RekorURL string
DetectionPriority ftypes.DetectionPriority
Distro ftypes.OS
NoNotices bool
}

func NewScanFlagGroup() *ScanFlagGroup {
Expand All @@ -161,6 +168,7 @@ func NewScanFlagGroup() *ScanFlagGroup {
Slow: SlowFlag.Clone(),
DetectionPriority: DetectionPriority.Clone(),
DistroFlag: DistroFlag.Clone(),
NoNotices: NoNoticesFlag.Clone(),
}
}

Expand All @@ -181,6 +189,7 @@ func (f *ScanFlagGroup) Flags() []Flagger {
f.RekorURL,
f.DetectionPriority,
f.DistroFlag,
f.NoNotices,
}
}

Expand Down Expand Up @@ -224,5 +233,6 @@ func (f *ScanFlagGroup) ToOptions(args []string) (ScanOptions, error) {
RekorURL: f.RekorURL.Value(),
DetectionPriority: ftypes.DetectionPriority(f.DetectionPriority.Value()),
Distro: distro,
NoNotices: f.NoNotices.Value(),
}, nil
}
13 changes: 13 additions & 0 deletions pkg/flag/scan_flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ func TestScanFlagGroup_ToOptions(t *testing.T) {
offlineScan bool
scanners string
distro string
noNotices bool
}
tests := []struct {
name string
Expand Down Expand Up @@ -127,6 +128,16 @@ func TestScanFlagGroup_ToOptions(t *testing.T) {
},
assertion: require.Error,
},
{
name: "no notices flag",
fields: fields{
noNotices: true,
},
want: flag.ScanOptions{
NoNotices: true,
},
assertion: require.NoError,
},
}

for _, tt := range tests {
Expand All @@ -137,6 +148,7 @@ func TestScanFlagGroup_ToOptions(t *testing.T) {
setValue(flag.OfflineScanFlag.ConfigName, tt.fields.offlineScan)
setValue(flag.ScannersFlag.ConfigName, tt.fields.scanners)
setValue(flag.DistroFlag.ConfigName, tt.fields.distro)
setValue(flag.NoNoticesFlag.ConfigName, tt.fields.noNotices)

// Assert options
f := &flag.ScanFlagGroup{
Expand All @@ -145,6 +157,7 @@ func TestScanFlagGroup_ToOptions(t *testing.T) {
OfflineScan: flag.OfflineScanFlag.Clone(),
Scanners: flag.ScannersFlag.Clone(),
DistroFlag: flag.DistroFlag.Clone(),
NoNotices: flag.NoNoticesFlag.Clone(),
}

got, err := f.ToOptions(tt.args)
Expand Down
46 changes: 46 additions & 0 deletions pkg/notification/identifier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package notification

import (
"crypto/sha256"
"fmt"
"net"
"os"
"strings"
)

func getMachineIdentifier() (string, error) {
hostname, err := os.Hostname()
if err != nil {
return "", err
}
interfaces, err := net.Interfaces()
if err != nil {
return "", err
}

var macAddr string
for _, iface := range interfaces {
if iface.HardwareAddr.String() != "" {
macAddr = iface.HardwareAddr.String()
break
}
}
identifier := fmt.Sprintf("%s-%s-%s", hostname, macAddr, strings.ToLower(hostname))

return identifier, nil
}

func generateMachineHash(identifier string) string {
hash := sha256.New()
hash.Write([]byte(identifier))
return fmt.Sprintf("%x", hash.Sum(nil))
}

func uniqueIdentifier() string {
identifier, err := getMachineIdentifier()
if err != nil {
return ""
}

return generateMachineHash(fmt.Sprintf("trivy-%s", identifier))
}
28 changes: 28 additions & 0 deletions pkg/notification/identifier_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package notification

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestGenerateMachineHash(t *testing.T) {
// Test with known input
identifier := "test-identifier"
hash := generateMachineHash(identifier)

// Known hash for "test-identifier"
expectedHash := "115ae872eb1d3e23f9de03f7ab344193b21068812ee52eb37e8169e6d093c7ae"
assert.Equal(t, expectedHash, hash)
}

// This test requires some modification to the original code to make it testable
// by injecting mocked network interfaces
func TestGetMachineIdentifier(t *testing.T) {
// This is a basic test that at least ensures the function returns without error
// A more complete test would mock os.Hostname and net.Interfaces
identifier, err := getMachineIdentifier()
require.NoError(t, err)
require.NotEmpty(t, identifier)
}
Loading
Loading