mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-21 14:50:53 -08:00
Compare commits
15 Commits
v0.64.1
...
feat/rooti
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e988fbfebf | ||
|
|
2da74d6aaa | ||
|
|
b8853b242f | ||
|
|
50d153b495 | ||
|
|
9b35e6b11f | ||
|
|
71ab2b8926 | ||
|
|
978276ddd0 | ||
|
|
3b54435520 | ||
|
|
2ed3d7e6c2 | ||
|
|
1885e35fa6 | ||
|
|
4d85a82749 | ||
|
|
680358743f | ||
|
|
c902f4ba90 | ||
|
|
44f068caa2 | ||
|
|
317fa2fe85 |
1
.github/workflows/semantic-pr.yaml
vendored
1
.github/workflows/semantic-pr.yaml
vendored
@@ -67,7 +67,6 @@ jobs:
|
||||
distroless
|
||||
windows
|
||||
minimos
|
||||
rootio
|
||||
|
||||
# Languages
|
||||
ruby
|
||||
|
||||
@@ -1 +1 @@
|
||||
{".":"0.64.1"}
|
||||
{".":"0.63.0"}
|
||||
|
||||
42
CHANGELOG.md
42
CHANGELOG.md
@@ -1,47 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## [0.64.1](https://github.com/aquasecurity/trivy/compare/v0.64.0...v0.64.1) (2025-07-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **alma:** parse epochs from rpmqa file [backport: release/v0.64] ([#9119](https://github.com/aquasecurity/trivy/issues/9119)) ([8cf1bf9](https://github.com/aquasecurity/trivy/commit/8cf1bf9f6f86936ee7dcd29e0d1cd1ec106e28f6))
|
||||
* **cli:** Add more non-sensitive flags to telemetry [backport: release/v0.64] ([#9124](https://github.com/aquasecurity/trivy/issues/9124)) ([9a7d384](https://github.com/aquasecurity/trivy/commit/9a7d38432cf00f00970259e5ac3edd060e00ccff))
|
||||
* **misconf:** skip rewriting expr if attr is nil [backport: release/v0.64] ([#9127](https://github.com/aquasecurity/trivy/issues/9127)) ([4e12722](https://github.com/aquasecurity/trivy/commit/4e1272283a643bfca2d7231d286006219715fada))
|
||||
* **rootio:** check full version to detect `root.io` packages [backport: release/v0.64] ([#9120](https://github.com/aquasecurity/trivy/issues/9120)) ([53adfba](https://github.com/aquasecurity/trivy/commit/53adfba3c25664b01e3a36fdec334b39b53c07f1))
|
||||
|
||||
## [0.64.0](https://github.com/aquasecurity/trivy/compare/v0.63.0...v0.64.0) (2025-06-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **cli:** add version constraints to annoucements ([#9023](https://github.com/aquasecurity/trivy/issues/9023)) ([19efa9f](https://github.com/aquasecurity/trivy/commit/19efa9fd372242d2ec582a248e9e6573d2caef00))
|
||||
* **java:** dereference all maven settings.xml env placeholders ([#9024](https://github.com/aquasecurity/trivy/issues/9024)) ([5aade69](https://github.com/aquasecurity/trivy/commit/5aade698c71450badf8db028be61e12ec85c6248))
|
||||
* **misconf:** add OpenTofu file extension support ([#8747](https://github.com/aquasecurity/trivy/issues/8747)) ([57801d0](https://github.com/aquasecurity/trivy/commit/57801d0324384d990889ba39d856c881e5b8b070))
|
||||
* **misconf:** normalize CreatedBy for buildah and legacy docker builder ([#8953](https://github.com/aquasecurity/trivy/issues/8953)) ([65e155f](https://github.com/aquasecurity/trivy/commit/65e155fdaf0ad02ec82f00a004427f126faf65ed))
|
||||
* **redhat:** Add EOL date for RHEL 10. ([#8910](https://github.com/aquasecurity/trivy/issues/8910)) ([48258a7](https://github.com/aquasecurity/trivy/commit/48258a701a7adb210c433310de52f48568ccee19))
|
||||
* reject unsupported artifact types in remote image retrieval ([#9052](https://github.com/aquasecurity/trivy/issues/9052)) ([1e1e1b5](https://github.com/aquasecurity/trivy/commit/1e1e1b5fa6a884da978fe1ed4c222d613d6eafbd))
|
||||
* **sbom:** add manufacturer field to CycloneDX tools metadata ([#9019](https://github.com/aquasecurity/trivy/issues/9019)) ([41d0f94](https://github.com/aquasecurity/trivy/commit/41d0f949c874609641c08fa2620fa10bf4ceef78))
|
||||
* **terraform:** add partial evaluation for policy templates ([#8967](https://github.com/aquasecurity/trivy/issues/8967)) ([a9f7dcd](https://github.com/aquasecurity/trivy/commit/a9f7dcdb9c5973746c3737f2bbc3306a74be5408))
|
||||
* **ubuntu:** add end of life date for Ubuntu 25.04 ([#9077](https://github.com/aquasecurity/trivy/issues/9077)) ([367564a](https://github.com/aquasecurity/trivy/commit/367564a3bec0c202566c59598dcff087bf50a23d))
|
||||
* **ubuntu:** add eol date for 20.04-ESM ([#8981](https://github.com/aquasecurity/trivy/issues/8981)) ([87118a0](https://github.com/aquasecurity/trivy/commit/87118a0ec4a6ae492523b7bac9834c2b93a14557))
|
||||
* **vuln:** add Root.io support for container image scanning ([#9073](https://github.com/aquasecurity/trivy/issues/9073)) ([3a0ec0f](https://github.com/aquasecurity/trivy/commit/3a0ec0f2acff6a13ed6ab348b6b220d49e14a298))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Add missing version check flags ([#8951](https://github.com/aquasecurity/trivy/issues/8951)) ([ef5f8de](https://github.com/aquasecurity/trivy/commit/ef5f8de8dadf5534a2c965aecca01c7067e5baca))
|
||||
* **cli:** add some values to the telemetry call ([#9056](https://github.com/aquasecurity/trivy/issues/9056)) ([fd2bc91](https://github.com/aquasecurity/trivy/commit/fd2bc91e133f846bc9f0910c19ac3be3fbfe4009))
|
||||
* Correctly check for semver versions for trivy version check ([#8948](https://github.com/aquasecurity/trivy/issues/8948)) ([b813527](https://github.com/aquasecurity/trivy/commit/b813527449c4604f5afad71ae82b13399bb48680))
|
||||
* don't show corrupted trivy-db warning for first run ([#8991](https://github.com/aquasecurity/trivy/issues/8991)) ([4ed78e3](https://github.com/aquasecurity/trivy/commit/4ed78e39afe57e81c12482fef9102dc3f85d1493))
|
||||
* **misconf:** .Config.User always takes precedence over USER in .History ([#9050](https://github.com/aquasecurity/trivy/issues/9050)) ([371b8cc](https://github.com/aquasecurity/trivy/commit/371b8cc02f2ffa3f42534a437ce8727519e7b9b9))
|
||||
* **misconf:** correct Azure value-to-time conversion in AsTimeValue ([#9015](https://github.com/aquasecurity/trivy/issues/9015)) ([40d017b](https://github.com/aquasecurity/trivy/commit/40d017b67da38131734eab90c42ad945ac3b5013))
|
||||
* **misconf:** move disabled checks filtering after analyzer scan ([#9002](https://github.com/aquasecurity/trivy/issues/9002)) ([a58c36d](https://github.com/aquasecurity/trivy/commit/a58c36de124cba7250e1a5ae0cc32d83018391fe))
|
||||
* **misconf:** reduce log noise on incompatible check ([#9029](https://github.com/aquasecurity/trivy/issues/9029)) ([99c5151](https://github.com/aquasecurity/trivy/commit/99c5151d6ea1dabe85cce75ff9bb91166532b11f))
|
||||
* **nodejs:** correctly parse `packages` array of `bun.lock` file ([#8998](https://github.com/aquasecurity/trivy/issues/8998)) ([875ec3a](https://github.com/aquasecurity/trivy/commit/875ec3a9d2568e15a6824c8f84ad6a59f03eb212))
|
||||
* **report:** don't panic when report contains vulns, but doesn't contain packages for `table` format ([#8549](https://github.com/aquasecurity/trivy/issues/8549)) ([87fda76](https://github.com/aquasecurity/trivy/commit/87fda76f38a3a6939a87828c3df0c5ac2cf7fce3))
|
||||
* **sbom:** remove unnecessary OS detection check in SBOM decoding ([#9034](https://github.com/aquasecurity/trivy/issues/9034)) ([198789a](https://github.com/aquasecurity/trivy/commit/198789a07b857b053c73f8fcd1f508902fac344d))
|
||||
|
||||
## [0.63.0](https://github.com/aquasecurity/trivy/compare/v0.62.0...v0.63.0) (2025-05-29)
|
||||
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
```
|
||||
--clear-cache
|
||||
--debug
|
||||
--dependency-tree
|
||||
--detection-priority
|
||||
--distro
|
||||
--exit-code
|
||||
--exit-on-eol
|
||||
--format
|
||||
--ignore-status
|
||||
--ignore-unfixed
|
||||
--image-config-scanners
|
||||
--include-deprecated-checks
|
||||
--include-dev-deps
|
||||
--include-non-failures
|
||||
--insecure
|
||||
--license-full
|
||||
--list-all-pkgs
|
||||
--misconfig-scanners
|
||||
--offline-scan
|
||||
--parallel
|
||||
--password-stdin
|
||||
--pkg-relationships
|
||||
--pkg-types
|
||||
--quiet
|
||||
--redis-tls
|
||||
--removed-pkgs
|
||||
--report
|
||||
--scanners
|
||||
--severity
|
||||
--show-suppressed
|
||||
--skip-check-update
|
||||
--skip-version-check
|
||||
--skip-vex-repo-update
|
||||
--slow
|
||||
--tf-exclude-downloaded-modules
|
||||
--timeout
|
||||
--trace
|
||||
--vuln-severity-source
|
||||
```
|
||||
@@ -1,30 +1,24 @@
|
||||
# Usage Telemetry
|
||||
|
||||
Trivy collects anonymous usage data in order to help us improve the product. This document explains what is collected and how you can control it.
|
||||
Trivy collect anonymous usage data in order to help us improve the product. This document explains what is collected and how you can control it.
|
||||
|
||||
## Data collected
|
||||
|
||||
The following information could be collected:
|
||||
|
||||
- Environmental information:
|
||||
- Installation identifier
|
||||
- Trivy version
|
||||
- Operating system
|
||||
- Scan:
|
||||
- Non-revealing scan options (see below for comprehensive list)
|
||||
|
||||
### Captured scan options
|
||||
The following flags will be included with their value:
|
||||
|
||||
--8<-- "./docs/docs/advanced/telemetry-flags.md"
|
||||
|
||||
- Environmental information
|
||||
- Installation identifier
|
||||
- Trivy version
|
||||
- Operating system
|
||||
- Scan
|
||||
- Non-revealing scan options
|
||||
|
||||
## Privacy
|
||||
|
||||
No personal information, scan results, or sensitive data is specifically collected. We take the following measures to ensure that:
|
||||
|
||||
- Installation identifier: one-way hash of machine fingerprint, resulting in opaque ID.
|
||||
- Scan: any option that is user-controlled is omitted (never collected). For example, file paths, image names, etc are never collected.
|
||||
- Installation identifier: one-way hash of machine fingerprint, resulting in opaque string.
|
||||
- Scaner: any option that is user controlled is omitted (never collected). For example, file paths, image names, etc are never collected.
|
||||
|
||||
Trivy is an Aqua Security product and adheres to the company's privacy policy: <https://aquasec.com/privacy>.
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ Example: [Dockerfile](https://github.com/aquasecurity/trivy-ci-test/blob/main/Do
|
||||
[license]: ../../scanner/license.md
|
||||
|
||||
[^1]: `*.egg-info`, `*.egg-info/PKG-INFO`, `*.egg` and `EGG-INFO/PKG-INFO`
|
||||
[^2]: `.dist-info/METADATA`
|
||||
[^2]: `.dist-info/META-DATA`
|
||||
[^3]: `*.jar`, `*.war`, `*.par` and `*.ear`
|
||||
[^4]: ✅ means "enabled" and `-` means "disabled" in the image scanning
|
||||
[^5]: ✅ means "enabled" and `-` means "disabled" in the rootfs scanning
|
||||
|
||||
@@ -148,7 +148,7 @@ See [here](https://packaging.python.org/en/latest/discussions/package-formats/)
|
||||
Trivy looks for `*.egg-info`, `*.egg-info/PKG-INFO`, `*.egg` and `EGG-INFO/PKG-INFO` to identify Python packages.
|
||||
|
||||
### Wheel
|
||||
Trivy looks for `.dist-info/METADATA` to identify Python packages.
|
||||
Trivy looks for `.dist-info/META-DATA` to identify Python packages.
|
||||
|
||||
[^1]: Trivy checks `python`, `python3`, `python2` and `python.exe` file names.
|
||||
|
||||
|
||||
7
go.mod
7
go.mod
@@ -24,7 +24,7 @@ require (
|
||||
github.com/aquasecurity/testdocker v0.0.0-20250616060700-ba6845ac6d17
|
||||
github.com/aquasecurity/tml v0.6.1
|
||||
github.com/aquasecurity/trivy-checks v1.11.3-0.20250604022615-9a7efa7c9169
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20250627124416-ca81c496a932
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20250529093513-a12dfc204b6e
|
||||
github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48
|
||||
github.com/aquasecurity/trivy-kubernetes v0.9.0
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.5
|
||||
@@ -248,7 +248,7 @@ require (
|
||||
github.com/go-openapi/strfmt v0.23.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.1 // indirect
|
||||
github.com/go-openapi/validate v0.24.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/goccy/go-yaml v1.15.23 // indirect
|
||||
github.com/gofrs/uuid v4.3.1+incompatible // indirect
|
||||
@@ -446,3 +446,6 @@ tool (
|
||||
golang.org/x/tools/cmd/goyacc
|
||||
sigs.k8s.io/kind
|
||||
)
|
||||
|
||||
// TODO: Delete it once https://github.com/aquasecurity/trivy-db/pull/546 gets merged.
|
||||
replace github.com/aquasecurity/trivy-db => github.com/chait-slim/trivy-db v0.0.0-20250626121835-ca687309f19d
|
||||
|
||||
8
go.sum
8
go.sum
@@ -798,8 +798,6 @@ github.com/aquasecurity/tml v0.6.1 h1:y2ZlGSfrhnn7t4ZJ/0rotuH+v5Jgv6BDDO5jB6A9gw
|
||||
github.com/aquasecurity/tml v0.6.1/go.mod h1:OnYMWY5lvI9ejU7yH9LCberWaaTBW7hBFsITiIMY2yY=
|
||||
github.com/aquasecurity/trivy-checks v1.11.3-0.20250604022615-9a7efa7c9169 h1:TckzIxUX7lZaU9f2lNxCN0noYYP8fzmSQf6a4JdV83w=
|
||||
github.com/aquasecurity/trivy-checks v1.11.3-0.20250604022615-9a7efa7c9169/go.mod h1:nT69xgRcBD4NlHwTBpWMYirpK5/Zpl8M+XDOgmjMn2k=
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20250627124416-ca81c496a932 h1:5GKQ53uGGHEEtZ/FX94jcIdfEGeyiHZ7tmJ5nCtDz5c=
|
||||
github.com/aquasecurity/trivy-db v0.0.0-20250627124416-ca81c496a932/go.mod h1:Ubl2YWA6Zg7eaojg4MDmeDdYU4+PiGPsnwo6B5UIwqw=
|
||||
github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 h1:JVgBIuIYbwG+ekC5lUHUpGJboPYiCcxiz06RCtz8neI=
|
||||
github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48/go.mod h1:Ldya37FLi0e/5Cjq2T5Bty7cFkzUDwTcPeQua+2M8i8=
|
||||
github.com/aquasecurity/trivy-kubernetes v0.9.0 h1:rp8RuXwKfFWUPR/ULksA2WpD0z6rslVkzLmPGQr61Wc=
|
||||
@@ -893,6 +891,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk=
|
||||
github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA=
|
||||
github.com/chait-slim/trivy-db v0.0.0-20250626121835-ca687309f19d h1:IgXL9bezFPnr8ttFN3yTciRDCdvOKZ6KFgsMOEBjiGE=
|
||||
github.com/chait-slim/trivy-db v0.0.0-20250626121835-ca687309f19d/go.mod h1:Ubl2YWA6Zg7eaojg4MDmeDdYU4+PiGPsnwo6B5UIwqw=
|
||||
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
|
||||
github.com/cheggaaa/pb/v3 v3.1.7 h1:2FsIW307kt7A/rz/ZI2lvPO+v3wKazzE4K/0LtTWsOI=
|
||||
github.com/cheggaaa/pb/v3 v3.1.7/go.mod h1:/Ji89zfVPeC/u5j8ukD0MBPHt2bzTYp74lQ7KlgFWTQ=
|
||||
@@ -1168,8 +1168,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
|
||||
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk=
|
||||
github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
"components": [
|
||||
{
|
||||
"type": "application",
|
||||
"manufacturer": {
|
||||
"name": "Aqua Security Software Ltd."
|
||||
},
|
||||
"group": "aquasecurity",
|
||||
"name": "trivy",
|
||||
"version": "dev"
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
"components": [
|
||||
{
|
||||
"type": "application",
|
||||
"manufacturer": {
|
||||
"name": "Aqua Security Software Ltd."
|
||||
},
|
||||
"group": "aquasecurity",
|
||||
"name": "trivy",
|
||||
"version": "dev"
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
"components": [
|
||||
{
|
||||
"type": "application",
|
||||
"manufacturer": {
|
||||
"name": "Aqua Security Software Ltd."
|
||||
},
|
||||
"group": "aquasecurity",
|
||||
"name": "trivy",
|
||||
"version": "dev"
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
"components": [
|
||||
{
|
||||
"type": "application",
|
||||
"manufacturer": {
|
||||
"name": "Aqua Security Software Ltd."
|
||||
},
|
||||
"group": "aquasecurity",
|
||||
"name": "trivy",
|
||||
"version": "dev"
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
"components": [
|
||||
{
|
||||
"type": "application",
|
||||
"manufacturer": {
|
||||
"name": "Aqua Security Software Ltd."
|
||||
},
|
||||
"group": "aquasecurity",
|
||||
"name": "trivy",
|
||||
"version": "dev"
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
"components": [
|
||||
{
|
||||
"type": "application",
|
||||
"manufacturer": {
|
||||
"name": "Aqua Security Software Ltd."
|
||||
},
|
||||
"group": "aquasecurity",
|
||||
"name": "trivy",
|
||||
"version": "dev"
|
||||
|
||||
@@ -7,13 +7,13 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra/doc"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/commands"
|
||||
"github.com/aquasecurity/trivy/pkg/flag"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/spf13/cobra/doc"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -35,43 +35,50 @@ func main() {
|
||||
os.Setenv("XDG_DATA_HOME", os.TempDir())
|
||||
|
||||
cmd := commands.NewApp()
|
||||
allFlagGroups := getAllFlags()
|
||||
|
||||
cmd.DisableAutoGenTag = true
|
||||
if err := doc.GenMarkdownTree(cmd, "./docs/docs/references/configuration/cli"); err != nil {
|
||||
log.Fatal("Fatal error", log.Err(err))
|
||||
}
|
||||
if err := generateConfigDocs("./docs/docs/references/configuration/config-file.md", allFlagGroups); err != nil {
|
||||
if err := generateConfigDocs("./docs/docs/references/configuration/config-file.md"); err != nil {
|
||||
log.Fatal("Fatal error in config file generation", log.Err(err))
|
||||
}
|
||||
if err := generateTelemetryFlagDocs("./docs/docs/advanced/telemetry-flags.md", allFlagGroups); err != nil {
|
||||
log.Fatal("Fatal error in telemetry docs generation", log.Err(err))
|
||||
}
|
||||
}
|
||||
|
||||
// generateTelemetryFlagDocs updates the telemetry section in the documentation file
|
||||
// with the flags that are safe to be included in telemetry.
|
||||
func generateTelemetryFlagDocs(filename string, allFlagGroups []flag.FlagGroup) error {
|
||||
var telemetryFlags []string
|
||||
for _, group := range allFlagGroups {
|
||||
flags := group.Flags()
|
||||
for _, f := range flags {
|
||||
if f.IsTelemetrySafe() && f.GetConfigName() != "" {
|
||||
telemetryFlags = append(telemetryFlags, fmt.Sprintf("--%s", f.GetName()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(telemetryFlags)
|
||||
flagContent := fmt.Sprintf("```\n%s\n```\n", strings.Join(telemetryFlags, "\n"))
|
||||
if err := os.WriteFile(filename, []byte(flagContent), 0644); err != nil {
|
||||
return fmt.Errorf("failed to write to %s: %w", filename, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// generateConfigDocs creates markdown file for Trivy config.
|
||||
func generateConfigDocs(filename string, allFlagGroups []flag.FlagGroup) error {
|
||||
func generateConfigDocs(filename string) error {
|
||||
// remoteFlags should contain Client and Server flags.
|
||||
// NewClientFlags doesn't initialize `Listen` field
|
||||
remoteFlags := flag.NewClientFlags()
|
||||
remoteFlags.Listen = flag.ServerListenFlag.Clone()
|
||||
|
||||
// These flags don't work from config file.
|
||||
// Clear configName to skip them later.
|
||||
globalFlags := flag.NewGlobalFlagGroup()
|
||||
globalFlags.ConfigFile.ConfigName = ""
|
||||
globalFlags.ShowVersion.ConfigName = ""
|
||||
globalFlags.GenerateDefaultConfig.ConfigName = ""
|
||||
|
||||
var allFlagGroups = []flag.FlagGroup{
|
||||
globalFlags,
|
||||
flag.NewCacheFlagGroup(),
|
||||
flag.NewCleanFlagGroup(),
|
||||
remoteFlags,
|
||||
flag.NewDBFlagGroup(),
|
||||
flag.NewImageFlagGroup(),
|
||||
flag.NewK8sFlagGroup(),
|
||||
flag.NewLicenseFlagGroup(),
|
||||
flag.NewMisconfFlagGroup(),
|
||||
flag.NewModuleFlagGroup(),
|
||||
flag.NewPackageFlagGroup(),
|
||||
flag.NewRegistryFlagGroup(),
|
||||
flag.NewRegoFlagGroup(),
|
||||
flag.NewReportFlagGroup(),
|
||||
flag.NewRepoFlagGroup(),
|
||||
flag.NewScanFlagGroup(),
|
||||
flag.NewSecretFlagGroup(),
|
||||
flag.NewVulnerabilityFlagGroup(),
|
||||
}
|
||||
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -80,10 +87,6 @@ func generateConfigDocs(filename string, allFlagGroups []flag.FlagGroup) error {
|
||||
f.WriteString("# " + title + "\n\n")
|
||||
f.WriteString(description + "\n")
|
||||
|
||||
if len(allFlagGroups) == 0 {
|
||||
return fmt.Errorf("no flag groups found")
|
||||
}
|
||||
|
||||
for _, group := range allFlagGroups {
|
||||
f.WriteString("## " + group.Name() + " options\n")
|
||||
writeFlags(group, f)
|
||||
@@ -158,39 +161,3 @@ func writeFlagValue(val any, ind string, w *os.File) {
|
||||
fmt.Fprintf(w, " %v\n", v)
|
||||
}
|
||||
}
|
||||
|
||||
func getAllFlags() []flag.FlagGroup {
|
||||
// remoteFlags should contain Client and Server flags.
|
||||
// NewClientFlags doesn't initialize `Listen` field
|
||||
remoteFlags := flag.NewClientFlags()
|
||||
remoteFlags.Listen = flag.ServerListenFlag.Clone()
|
||||
|
||||
// These flags don't work from config file.
|
||||
// Clear configName to skip them later.
|
||||
globalFlags := flag.NewGlobalFlagGroup()
|
||||
globalFlags.ConfigFile.ConfigName = ""
|
||||
globalFlags.ShowVersion.ConfigName = ""
|
||||
globalFlags.GenerateDefaultConfig.ConfigName = ""
|
||||
|
||||
return []flag.FlagGroup{
|
||||
globalFlags,
|
||||
flag.NewCacheFlagGroup(),
|
||||
flag.NewCleanFlagGroup(),
|
||||
remoteFlags,
|
||||
flag.NewDBFlagGroup(),
|
||||
flag.NewImageFlagGroup(),
|
||||
flag.NewK8sFlagGroup(),
|
||||
flag.NewLicenseFlagGroup(),
|
||||
flag.NewMisconfFlagGroup(),
|
||||
flag.NewModuleFlagGroup(),
|
||||
flag.NewPackageFlagGroup(),
|
||||
flag.NewRegistryFlagGroup(),
|
||||
flag.NewRegoFlagGroup(),
|
||||
flag.NewReportFlagGroup(),
|
||||
flag.NewRepoFlagGroup(),
|
||||
flag.NewScanFlagGroup(),
|
||||
flag.NewSecretFlagGroup(),
|
||||
flag.NewVulnerabilityFlagGroup(),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -263,7 +263,6 @@ markdown_extensions:
|
||||
- pymdownx.highlight
|
||||
- pymdownx.details
|
||||
- pymdownx.magiclink
|
||||
- pymdownx.snippets
|
||||
- pymdownx.superfences:
|
||||
custom_fences:
|
||||
- name: mermaid
|
||||
|
||||
@@ -124,9 +124,13 @@ func NewRunner(ctx context.Context, cliOptions flag.Options, opts ...RunnerOptio
|
||||
Insecure: cliOptions.Insecure,
|
||||
Timeout: cliOptions.Timeout,
|
||||
}))
|
||||
// get the sub command that is being used or fallback to "trivy"
|
||||
commandName := lo.Ternary(len(os.Args) > 1, os.Args[1], "trivy")
|
||||
r.versionChecker = notification.NewVersionChecker(commandName, &cliOptions)
|
||||
|
||||
// If the user has not disabled notices or is running in quiet mode
|
||||
r.versionChecker = notification.NewVersionChecker(
|
||||
notification.WithSkipVersionCheck(cliOptions.SkipVersionCheck),
|
||||
notification.WithQuietMode(cliOptions.Quiet),
|
||||
notification.WithTelemetryDisabled(cliOptions.DisableTelemetry),
|
||||
)
|
||||
|
||||
// Update the vulnerability database if needed.
|
||||
if err := r.initDB(ctx, cliOptions); err != nil {
|
||||
@@ -153,7 +157,7 @@ func NewRunner(ctx context.Context, cliOptions flag.Options, opts ...RunnerOptio
|
||||
// only do this if the user has not disabled notices or is running
|
||||
// in quiet mode
|
||||
if r.versionChecker != nil {
|
||||
r.versionChecker.RunUpdateCheck(ctx)
|
||||
r.versionChecker.RunUpdateCheck(ctx, os.Args[1:])
|
||||
}
|
||||
|
||||
return r, nil
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/detector/ospkg/driver"
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/scan/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -38,7 +37,7 @@ func isRootIOEnvironment(osFamily ftypes.OSType, pkgs []ftypes.Package) bool {
|
||||
// hasPackageWithPattern checks if any package version matches the specified pattern
|
||||
func hasPackageWithPattern(pkgs []ftypes.Package, pattern *regexp.Regexp) bool {
|
||||
for _, pkg := range pkgs {
|
||||
if pattern.MatchString(utils.FormatVersion(pkg)) {
|
||||
if pattern.MatchString(pkg.Version) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ func TestProvider(t *testing.T) {
|
||||
name: "Debian with .root.io package",
|
||||
osFamily: ftypes.Debian,
|
||||
pkgs: []ftypes.Package{
|
||||
{Name: "libc6", Version: "2.31", Release: "13+deb11u4.root.io"},
|
||||
{Name: "libc6", Version: "2.31-13+deb11u4.root.io"},
|
||||
{Name: "bash", Version: "5.1-2+deb11u1"},
|
||||
},
|
||||
want: true,
|
||||
|
||||
@@ -18,51 +18,41 @@ import (
|
||||
|
||||
// Scanner implements the Root.io scanner
|
||||
type Scanner struct {
|
||||
comparer version.Comparer
|
||||
vsg rootio.VulnSrcGetter
|
||||
versionTrimmer func(string) string
|
||||
logger *log.Logger
|
||||
comparer version.Comparer
|
||||
vs rootio.VulnSrc
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
// NewScanner is the factory method for Scanner
|
||||
func NewScanner(baseOS ftypes.OSType) *Scanner {
|
||||
var comparer version.Comparer
|
||||
var vsg rootio.VulnSrcGetter
|
||||
var versionTrimmer func(string) string
|
||||
var vs rootio.VulnSrc
|
||||
|
||||
switch baseOS {
|
||||
case ftypes.Debian:
|
||||
comparer = version.NewDEBComparer()
|
||||
vsg = rootio.NewVulnSrcGetter(vulnerability.Debian)
|
||||
versionTrimmer = version.Major
|
||||
vs = rootio.NewVulnSrc(vulnerability.Debian)
|
||||
case ftypes.Ubuntu:
|
||||
comparer = version.NewDEBComparer()
|
||||
vsg = rootio.NewVulnSrcGetter(vulnerability.Ubuntu)
|
||||
versionTrimmer = version.Minor
|
||||
vs = rootio.NewVulnSrc(vulnerability.Ubuntu)
|
||||
case ftypes.Alpine:
|
||||
comparer = version.NewAPKComparer()
|
||||
vsg = rootio.NewVulnSrcGetter(vulnerability.Alpine)
|
||||
versionTrimmer = version.Minor
|
||||
vs = rootio.NewVulnSrc(vulnerability.Alpine)
|
||||
default:
|
||||
// Should never happen as it's validated in the provider
|
||||
comparer = version.NewDEBComparer()
|
||||
vsg = rootio.NewVulnSrcGetter(vulnerability.Debian)
|
||||
versionTrimmer = version.Major
|
||||
vs = rootio.NewVulnSrc(vulnerability.Debian)
|
||||
}
|
||||
|
||||
return &Scanner{
|
||||
comparer: comparer,
|
||||
vsg: vsg,
|
||||
versionTrimmer: versionTrimmer,
|
||||
logger: log.WithPrefix("rootio"),
|
||||
comparer: comparer,
|
||||
vs: vs,
|
||||
logger: log.WithPrefix("rootio"),
|
||||
}
|
||||
}
|
||||
|
||||
// Detect vulnerabilities in package using Root.io scanner
|
||||
func (s *Scanner) Detect(ctx context.Context, osVer string, _ *ftypes.Repository, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
|
||||
// Trim patch/minor part of osVer.
|
||||
// e.g. "12.0.1" -> "12" (Debian), "24.04.1" -> "24.04" (Ubuntu), "3.17.2" -> "3.17" (Alpine)
|
||||
osVer = s.versionTrimmer(osVer)
|
||||
log.InfoContext(ctx, "Detecting vulnerabilities...", log.String("os_version", osVer), log.Int("pkg_num", len(pkgs)))
|
||||
|
||||
var vulns []types.DetectedVulnerability
|
||||
@@ -72,7 +62,7 @@ func (s *Scanner) Detect(ctx context.Context, osVer string, _ *ftypes.Repository
|
||||
srcName = pkg.Name
|
||||
}
|
||||
|
||||
advisories, err := s.vsg.Get(osVer, srcName)
|
||||
advisories, err := s.vs.Get(osVer, srcName)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to get Root.io advisories: %w", err)
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ func TestScanner_Detect(t *testing.T) {
|
||||
"testdata/fixtures/data-source.yaml",
|
||||
},
|
||||
args: args{
|
||||
osVer: "3.19.3",
|
||||
osVer: "3.19",
|
||||
pkgs: []ftypes.Package{
|
||||
{
|
||||
Name: "less",
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"context"
|
||||
"os"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
@@ -51,7 +50,6 @@ func (a rpmqaPkgAnalyzer) parseRpmqaManifest(r xio.ReadSeekerAt) ([]types.Packag
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
var name, ver, rel, sourceRpm, arch string
|
||||
var epoch int
|
||||
// %{NAME}\t%{VERSION}-%{RELEASE}\t%{INSTALLTIME}\t%{BUILDTIME}\t%{VENDOR}\t(none)\t%{SIZE}\t%{ARCH}\t%{EPOCHNUM}\t%{SOURCERPM}
|
||||
s := strings.Split(line, "\t")
|
||||
if len(s) != 10 {
|
||||
@@ -70,18 +68,12 @@ func (a rpmqaPkgAnalyzer) parseRpmqaManifest(r xio.ReadSeekerAt) ([]types.Packag
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to split source rpm: %w", err)
|
||||
}
|
||||
epoch, err = strconv.Atoi(s[8])
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to parse epoch number (%s): %w", s[8], err)
|
||||
}
|
||||
pkgs = append(pkgs, types.Package{
|
||||
Name: name,
|
||||
Version: ver,
|
||||
Epoch: epoch,
|
||||
Release: rel,
|
||||
Arch: arch,
|
||||
SrcName: srcName,
|
||||
SrcEpoch: epoch,
|
||||
SrcVersion: srcVer,
|
||||
SrcRelease: srcRel,
|
||||
})
|
||||
|
||||
@@ -21,8 +21,7 @@ func TestParseMarinerDistrolessManifest(t *testing.T) {
|
||||
name: "happy path",
|
||||
content: `mariner-release 2.0-12.cm2 1653816591 1653753130 Microsoft Corporation (none) 580 noarch 0 mariner-release-2.0-12.cm2.src.rpm
|
||||
filesystem 1.1-9.cm2 1653816591 1653628924 Microsoft Corporation (none) 7596 x86_64 0 filesystem-1.1-9.cm2.src.rpm
|
||||
glibc 2.35-2.cm2 1653816591 1653628955 Microsoft Corporation (none) 10855265 x86_64 0 glibc-2.35-2.cm2.src.rpm
|
||||
ca-certificates-base 3.0.0-8.azl3 1748892790 1735838940 Microsoft Corporation (none) 130628 noarch 1 ca-certificates-3.0.0-8.azl3.src.rpm`,
|
||||
glibc 2.35-2.cm2 1653816591 1653628955 Microsoft Corporation (none) 10855265 x86_64 0 glibc-2.35-2.cm2.src.rpm`,
|
||||
wantPkgs: []types.Package{
|
||||
{
|
||||
Name: "mariner-release",
|
||||
@@ -51,17 +50,6 @@ ca-certificates-base 3.0.0-8.azl3 1748892790 1735838940 Microsoft Corporation (n
|
||||
SrcVersion: "2.35",
|
||||
SrcRelease: "2.cm2",
|
||||
},
|
||||
{
|
||||
Name: "ca-certificates-base",
|
||||
Version: "3.0.0",
|
||||
Epoch: 1,
|
||||
Release: "8.azl3",
|
||||
Arch: "noarch",
|
||||
SrcName: "ca-certificates",
|
||||
SrcEpoch: 1,
|
||||
SrcVersion: "3.0.0",
|
||||
SrcRelease: "8.azl3",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/remote"
|
||||
@@ -21,11 +20,6 @@ func tryRemote(ctx context.Context, imageName string, ref name.Reference, option
|
||||
if err != nil {
|
||||
return nil, cleanup, err
|
||||
}
|
||||
// ArtifactType being non-empty indicates this is not a regular container image
|
||||
// (e.g., Helm charts, WASM modules, or other OCI artifacts)
|
||||
if desc.ArtifactType != "" {
|
||||
return nil, cleanup, xerrors.Errorf("unsupported artifact type %q for image %q", desc.ArtifactType, imageName)
|
||||
}
|
||||
img, err := desc.Image()
|
||||
if err != nil {
|
||||
return nil, cleanup, err
|
||||
|
||||
@@ -1,21 +1,11 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
"github.com/google/go-containerregistry/pkg/registry"
|
||||
"github.com/google/go-containerregistry/pkg/v1/mutate"
|
||||
"github.com/google/go-containerregistry/pkg/v1/random"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func Test_implicitReference_TagName(t *testing.T) {
|
||||
@@ -92,100 +82,3 @@ func Test_implicitReference_RepositoryName(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_tryRemote(t *testing.T) {
|
||||
// Create a test image
|
||||
img, err := random.Image(1024, 5)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Get the image digest for test expectations
|
||||
digest, err := img.Digest()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Set up registry server with null logger to suppress log output
|
||||
nullLogger := log.New(io.Discard, "", 0)
|
||||
s := httptest.NewServer(registry.New(registry.Logger(nullLogger)))
|
||||
t.Cleanup(s.Close)
|
||||
|
||||
u, err := url.Parse(s.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
imageName string
|
||||
setupImage func(t *testing.T, ref name.Reference)
|
||||
wantName string
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "successful image retrieval",
|
||||
imageName: "test/alpine:3.10",
|
||||
setupImage: func(t *testing.T, ref name.Reference) {
|
||||
err := remote.Write(ref, img)
|
||||
require.NoError(t, err)
|
||||
},
|
||||
wantName: "/test/alpine:3.10",
|
||||
},
|
||||
{
|
||||
name: "helm chart config media type",
|
||||
imageName: "test/helm:chart",
|
||||
setupImage: func(t *testing.T, ref name.Reference) {
|
||||
configFile, err := img.ConfigFile()
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a new config with helm chart media type
|
||||
imageToWrite, err := mutate.Config(img, configFile.Config)
|
||||
require.NoError(t, err)
|
||||
|
||||
imageToWrite = mutate.ConfigMediaType(imageToWrite, "application/vnd.cncf.helm.chart")
|
||||
|
||||
err = remote.Write(ref, imageToWrite)
|
||||
require.NoError(t, err)
|
||||
},
|
||||
wantErr: "unsupported artifact type",
|
||||
},
|
||||
{
|
||||
name: "image not found",
|
||||
imageName: "test/notfound:latest",
|
||||
wantErr: "NAME_UNKNOWN",
|
||||
setupImage: func(*testing.T, name.Reference) {},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Parse the image name with the test server address
|
||||
fullImageName := u.Host + "/" + tt.imageName
|
||||
ref, err := name.ParseReference(fullImageName)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Set up the image in registry if needed
|
||||
tt.setupImage(t, ref)
|
||||
|
||||
ctx := t.Context()
|
||||
got, cleanup, err := tryRemote(ctx, fullImageName, ref, types.ImageOptions{
|
||||
RegistryOptions: types.RegistryOptions{
|
||||
Insecure: true,
|
||||
},
|
||||
})
|
||||
t.Cleanup(cleanup)
|
||||
|
||||
if tt.wantErr != "" {
|
||||
assert.ErrorContains(t, err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, got)
|
||||
assert.Contains(t, got.Name(), tt.wantName)
|
||||
|
||||
// Verify RepoTags and RepoDigests contain expected values
|
||||
repoTags := got.RepoTags()
|
||||
repoDigests := got.RepoDigests()
|
||||
assert.Len(t, repoTags, 1)
|
||||
assert.Contains(t, repoTags[0], tt.imageName)
|
||||
assert.Len(t, repoDigests, 1)
|
||||
assert.Contains(t, repoDigests[0], digest.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,16 +12,14 @@ var (
|
||||
Usage: "AWS Endpoint override",
|
||||
}
|
||||
awsServiceFlag = Flag[[]string]{
|
||||
Name: "service",
|
||||
ConfigName: "cloud.aws.service",
|
||||
Usage: "Only scan AWS Service(s) specified with this flag. Can specify multiple services using --service A --service B etc.",
|
||||
TelemetrySafe: true,
|
||||
Name: "service",
|
||||
ConfigName: "cloud.aws.service",
|
||||
Usage: "Only scan AWS Service(s) specified with this flag. Can specify multiple services using --service A --service B etc.",
|
||||
}
|
||||
awsSkipServicesFlag = Flag[[]string]{
|
||||
Name: "skip-service",
|
||||
ConfigName: "cloud.aws.skip-service",
|
||||
Usage: "Skip selected AWS Service(s) specified with this flag. Can specify multiple services using --skip-service A --skip-service B etc.",
|
||||
TelemetrySafe: true,
|
||||
Name: "skip-service",
|
||||
ConfigName: "cloud.aws.skip-service",
|
||||
Usage: "Skip selected AWS Service(s) specified with this flag. Can specify multiple services using --skip-service A --skip-service B etc.",
|
||||
}
|
||||
awsAccountFlag = Flag[string]{
|
||||
Name: "account",
|
||||
|
||||
@@ -16,11 +16,10 @@ import (
|
||||
var (
|
||||
// Deprecated
|
||||
ClearCacheFlag = Flag[bool]{
|
||||
Name: "clear-cache",
|
||||
ConfigName: "cache.clear",
|
||||
Usage: "clear image caches without scanning",
|
||||
Removed: `Use "trivy clean --scan-cache" instead`,
|
||||
TelemetrySafe: true,
|
||||
Name: "clear-cache",
|
||||
ConfigName: "cache.clear",
|
||||
Usage: "clear image caches without scanning",
|
||||
Removed: `Use "trivy clean --scan-cache" instead`,
|
||||
}
|
||||
CacheBackendFlag = Flag[string]{
|
||||
Name: "cache-backend",
|
||||
@@ -34,10 +33,9 @@ var (
|
||||
Usage: "cache TTL when using redis as cache backend",
|
||||
}
|
||||
RedisTLSFlag = Flag[bool]{
|
||||
Name: "redis-tls",
|
||||
ConfigName: "cache.redis.tls",
|
||||
Usage: "enable redis TLS with public certificates, if using redis as cache backend",
|
||||
TelemetrySafe: true,
|
||||
Name: "redis-tls",
|
||||
ConfigName: "cache.redis.tls",
|
||||
Usage: "enable redis TLS with public certificates, if using redis as cache backend",
|
||||
}
|
||||
RedisCACertFlag = Flag[string]{
|
||||
Name: "redis-ca",
|
||||
|
||||
@@ -27,35 +27,31 @@ var (
|
||||
Persistent: true,
|
||||
}
|
||||
QuietFlag = Flag[bool]{
|
||||
Name: "quiet",
|
||||
ConfigName: "quiet",
|
||||
Shorthand: "q",
|
||||
Usage: "suppress progress bar and log output",
|
||||
Persistent: true,
|
||||
TelemetrySafe: true,
|
||||
Name: "quiet",
|
||||
ConfigName: "quiet",
|
||||
Shorthand: "q",
|
||||
Usage: "suppress progress bar and log output",
|
||||
Persistent: true,
|
||||
}
|
||||
DebugFlag = Flag[bool]{
|
||||
Name: "debug",
|
||||
ConfigName: "debug",
|
||||
Shorthand: "d",
|
||||
Usage: "debug mode",
|
||||
Persistent: true,
|
||||
TelemetrySafe: true,
|
||||
Name: "debug",
|
||||
ConfigName: "debug",
|
||||
Shorthand: "d",
|
||||
Usage: "debug mode",
|
||||
Persistent: true,
|
||||
}
|
||||
InsecureFlag = Flag[bool]{
|
||||
Name: "insecure",
|
||||
ConfigName: "insecure",
|
||||
Usage: "allow insecure server connections",
|
||||
Persistent: true,
|
||||
TelemetrySafe: true,
|
||||
Name: "insecure",
|
||||
ConfigName: "insecure",
|
||||
Usage: "allow insecure server connections",
|
||||
Persistent: true,
|
||||
}
|
||||
TimeoutFlag = Flag[time.Duration]{
|
||||
Name: "timeout",
|
||||
ConfigName: "timeout",
|
||||
Default: time.Second * 300, // 5 mins
|
||||
Usage: "timeout",
|
||||
Persistent: true,
|
||||
TelemetrySafe: true,
|
||||
Name: "timeout",
|
||||
ConfigName: "timeout",
|
||||
Default: time.Second * 300, // 5 mins
|
||||
Usage: "timeout",
|
||||
Persistent: true,
|
||||
}
|
||||
CacheDirFlag = Flag[string]{
|
||||
Name: "cache-dir",
|
||||
|
||||
@@ -23,14 +23,12 @@ var (
|
||||
types.MisconfigScanner,
|
||||
types.SecretScanner,
|
||||
}),
|
||||
Usage: "comma-separated list of what security issues to detect on container image configurations",
|
||||
TelemetrySafe: true,
|
||||
Usage: "comma-separated list of what security issues to detect on container image configurations",
|
||||
}
|
||||
ScanRemovedPkgsFlag = Flag[bool]{
|
||||
Name: "removed-pkgs",
|
||||
ConfigName: "image.removed-pkgs",
|
||||
Usage: "detect vulnerabilities of removed packages (only for Alpine)",
|
||||
TelemetrySafe: true,
|
||||
Name: "removed-pkgs",
|
||||
ConfigName: "image.removed-pkgs",
|
||||
Usage: "detect vulnerabilities of removed packages (only for Alpine)",
|
||||
}
|
||||
InputFlag = Flag[string]{
|
||||
Name: "input",
|
||||
|
||||
@@ -7,10 +7,9 @@ import (
|
||||
|
||||
var (
|
||||
LicenseFull = Flag[bool]{
|
||||
Name: "license-full",
|
||||
ConfigName: "license.full",
|
||||
Usage: "eagerly look for licenses in source code headers and license files",
|
||||
TelemetrySafe: true,
|
||||
Name: "license-full",
|
||||
ConfigName: "license.full",
|
||||
Usage: "eagerly look for licenses in source code headers and license files",
|
||||
}
|
||||
IgnoredLicenses = Flag[[]string]{
|
||||
Name: "ignored-licenses",
|
||||
|
||||
@@ -33,10 +33,9 @@ var (
|
||||
},
|
||||
}
|
||||
IncludeNonFailuresFlag = Flag[bool]{
|
||||
Name: "include-non-failures",
|
||||
ConfigName: "misconfiguration.include-non-failures",
|
||||
Usage: "include successes, available with '--scanners misconfig'",
|
||||
TelemetrySafe: true,
|
||||
Name: "include-non-failures",
|
||||
ConfigName: "misconfiguration.include-non-failures",
|
||||
Usage: "include successes, available with '--scanners misconfig'",
|
||||
}
|
||||
HelmValuesFileFlag = Flag[[]string]{
|
||||
Name: "helm-values",
|
||||
@@ -80,10 +79,9 @@ var (
|
||||
Usage: "specify paths to override the CloudFormation parameters files",
|
||||
}
|
||||
TerraformExcludeDownloaded = Flag[bool]{
|
||||
Name: "tf-exclude-downloaded-modules",
|
||||
ConfigName: "misconfiguration.terraform.exclude-downloaded-modules",
|
||||
Usage: "exclude misconfigurations for downloaded terraform modules",
|
||||
TelemetrySafe: true,
|
||||
Name: "tf-exclude-downloaded-modules",
|
||||
ConfigName: "misconfiguration.terraform.exclude-downloaded-modules",
|
||||
Usage: "exclude misconfigurations for downloaded terraform modules",
|
||||
}
|
||||
ChecksBundleRepositoryFlag = Flag[string]{
|
||||
Name: "checks-bundle-repository",
|
||||
@@ -104,8 +102,7 @@ var (
|
||||
Default: xstrings.ToStringSlice(
|
||||
lo.Without(analyzer.TypeConfigFiles, analyzer.TypeYAML, analyzer.TypeJSON),
|
||||
),
|
||||
Usage: "comma-separated list of misconfig scanners to use for misconfiguration scanning",
|
||||
TelemetrySafe: true,
|
||||
Usage: "comma-separated list of misconfig scanners to use for misconfiguration scanning",
|
||||
}
|
||||
ConfigFileSchemasFlag = Flag[[]string]{
|
||||
Name: "config-file-schemas",
|
||||
|
||||
@@ -76,9 +76,6 @@ type Flag[T FlagType] struct {
|
||||
// Aliases represents aliases
|
||||
Aliases []Alias
|
||||
|
||||
// TelemetrySafe indicates if the flag value is safe to be included in telemetry.
|
||||
TelemetrySafe bool
|
||||
|
||||
// value is the value passed through CLI flag, env, or config file.
|
||||
// It is populated after flag.Parse() is called.
|
||||
value T
|
||||
@@ -221,17 +218,6 @@ func (f *Flag[T]) GetAliases() []Alias {
|
||||
return f.Aliases
|
||||
}
|
||||
|
||||
func (f *Flag[T]) IsTelemetrySafe() bool {
|
||||
return f.TelemetrySafe
|
||||
}
|
||||
|
||||
func (f *Flag[T]) IsSet() bool {
|
||||
if f == nil {
|
||||
return false
|
||||
}
|
||||
return f.isSet()
|
||||
}
|
||||
|
||||
func (f *Flag[T]) Hidden() bool {
|
||||
return f.Deprecated != "" || f.Removed != "" || f.Internal
|
||||
}
|
||||
@@ -363,8 +349,6 @@ type Flagger interface {
|
||||
GetDefaultValue() any
|
||||
GetAliases() []Alias
|
||||
Hidden() bool
|
||||
IsTelemetrySafe() bool
|
||||
IsSet() bool
|
||||
|
||||
Parse() error
|
||||
Add(cmd *cobra.Command)
|
||||
@@ -407,9 +391,6 @@ type Options struct {
|
||||
|
||||
// args is the arguments passed to the command.
|
||||
args []string
|
||||
|
||||
// usedFlags allows us to get the underlying flags for the options
|
||||
usedFlags []Flagger
|
||||
}
|
||||
|
||||
// Align takes consistency of options
|
||||
@@ -574,11 +555,6 @@ func (o *Options) OutputWriter(ctx context.Context) (io.Writer, func() error, er
|
||||
return f, f.Close, nil
|
||||
}
|
||||
|
||||
// GetUsedFlags returns the explicitly set flags for the options.
|
||||
func (o *Options) GetUsedFlags() []Flagger {
|
||||
return o.usedFlags
|
||||
}
|
||||
|
||||
func (o *Options) outputPluginWriter(ctx context.Context) (io.Writer, func() error, error) {
|
||||
pluginName := strings.TrimPrefix(o.Output, "plugin=")
|
||||
|
||||
@@ -675,8 +651,6 @@ func (f *Flags) ToOptions(args []string) (Options, error) {
|
||||
return Options{}, xerrors.Errorf("unable to parse flags: %w", err)
|
||||
}
|
||||
|
||||
opts.usedFlags = append(opts.usedFlags, usedFlags(group)...)
|
||||
|
||||
if err := group.ToOptions(&opts); err != nil {
|
||||
return Options{}, xerrors.Errorf("unable to convert flags to options: %w", err)
|
||||
}
|
||||
@@ -777,21 +751,3 @@ func findFlagGroup[T FlagGroup](f *Flags) (T, bool) {
|
||||
var zero T
|
||||
return zero, false
|
||||
}
|
||||
|
||||
// usedFlags returns a slice of flags that are set in the given FlagGroup.
|
||||
func usedFlags(fg FlagGroup) []Flagger {
|
||||
if fg == nil || fg.Flags() == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var flags []Flagger
|
||||
for _, flag := range fg.Flags() {
|
||||
if flag == nil {
|
||||
continue
|
||||
}
|
||||
if flag.IsSet() {
|
||||
flags = append(flags, flag)
|
||||
}
|
||||
}
|
||||
return flags
|
||||
}
|
||||
|
||||
@@ -8,10 +8,9 @@ import (
|
||||
|
||||
var (
|
||||
IncludeDevDepsFlag = Flag[bool]{
|
||||
Name: "include-dev-deps",
|
||||
ConfigName: "pkg.include-dev-deps",
|
||||
Usage: "include development dependencies in the report (supported: npm, yarn, gradle)",
|
||||
TelemetrySafe: true,
|
||||
Name: "include-dev-deps",
|
||||
ConfigName: "pkg.include-dev-deps",
|
||||
Usage: "include development dependencies in the report (supported: npm, yarn, gradle)",
|
||||
}
|
||||
PkgTypesFlag = Flag[[]string]{
|
||||
Name: "pkg-types",
|
||||
@@ -26,15 +25,13 @@ var (
|
||||
Deprecated: true, // --vuln-type was renamed to --pkg-types
|
||||
},
|
||||
},
|
||||
TelemetrySafe: true,
|
||||
}
|
||||
PkgRelationshipsFlag = Flag[[]string]{
|
||||
Name: "pkg-relationships",
|
||||
ConfigName: "pkg.relationships",
|
||||
Default: xstrings.ToStringSlice(ftypes.Relationships),
|
||||
Values: xstrings.ToStringSlice(ftypes.Relationships),
|
||||
Usage: "list of package relationships",
|
||||
TelemetrySafe: true,
|
||||
Name: "pkg-relationships",
|
||||
ConfigName: "pkg.relationships",
|
||||
Default: xstrings.ToStringSlice(ftypes.Relationships),
|
||||
Values: xstrings.ToStringSlice(ftypes.Relationships),
|
||||
Usage: "list of package relationships",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -22,10 +22,9 @@ var (
|
||||
Usage: "password. Comma-separated passwords allowed. TRIVY_PASSWORD should be used for security reasons.",
|
||||
}
|
||||
PasswordStdinFlag = Flag[bool]{
|
||||
Name: "password-stdin",
|
||||
ConfigName: "registry.password-stdin",
|
||||
Usage: "password from stdin. Comma-separated passwords are not supported.",
|
||||
TelemetrySafe: true,
|
||||
Name: "password-stdin",
|
||||
ConfigName: "registry.password-stdin",
|
||||
Usage: "password from stdin. Comma-separated passwords are not supported.",
|
||||
}
|
||||
RegistryTokenFlag = Flag[string]{
|
||||
Name: "registry-token",
|
||||
|
||||
@@ -8,10 +8,9 @@ package flag
|
||||
// policy-namespaces: "user"
|
||||
var (
|
||||
IncludeDeprecatedChecksFlag = Flag[bool]{
|
||||
Name: "include-deprecated-checks",
|
||||
ConfigName: "rego.include-deprecated-checks",
|
||||
Usage: "include deprecated checks",
|
||||
TelemetrySafe: true,
|
||||
Name: "include-deprecated-checks",
|
||||
ConfigName: "rego.include-deprecated-checks",
|
||||
Usage: "include deprecated checks",
|
||||
}
|
||||
SkipCheckUpdateFlag = Flag[bool]{
|
||||
Name: "skip-check-update",
|
||||
@@ -24,13 +23,11 @@ var (
|
||||
Deprecated: true,
|
||||
},
|
||||
},
|
||||
TelemetrySafe: true,
|
||||
}
|
||||
TraceFlag = Flag[bool]{
|
||||
Name: "trace",
|
||||
ConfigName: "rego.trace",
|
||||
Usage: "enable more verbose trace output for custom queries",
|
||||
TelemetrySafe: true,
|
||||
Name: "trace",
|
||||
ConfigName: "rego.trace",
|
||||
Usage: "enable more verbose trace output for custom queries",
|
||||
}
|
||||
ConfigCheckFlag = Flag[[]string]{
|
||||
Name: "config-check",
|
||||
|
||||
@@ -26,13 +26,12 @@ import (
|
||||
// severity: HIGH,CRITICAL
|
||||
var (
|
||||
FormatFlag = Flag[string]{
|
||||
Name: "format",
|
||||
ConfigName: "format",
|
||||
Shorthand: "f",
|
||||
Default: string(types.FormatTable),
|
||||
Values: xstrings.ToStringSlice(types.SupportedFormats),
|
||||
Usage: "format",
|
||||
TelemetrySafe: true,
|
||||
Name: "format",
|
||||
ConfigName: "format",
|
||||
Shorthand: "f",
|
||||
Default: string(types.FormatTable),
|
||||
Values: xstrings.ToStringSlice(types.SupportedFormats),
|
||||
Usage: "format",
|
||||
}
|
||||
ReportFormatFlag = Flag[string]{
|
||||
Name: "report",
|
||||
@@ -42,8 +41,7 @@ var (
|
||||
"all",
|
||||
"summary",
|
||||
},
|
||||
Usage: "specify a report format for the output",
|
||||
TelemetrySafe: true,
|
||||
Usage: "specify a report format for the output",
|
||||
}
|
||||
TemplateFlag = Flag[string]{
|
||||
Name: "template",
|
||||
@@ -52,16 +50,14 @@ var (
|
||||
Usage: "output template",
|
||||
}
|
||||
DependencyTreeFlag = Flag[bool]{
|
||||
Name: "dependency-tree",
|
||||
ConfigName: "dependency-tree",
|
||||
Usage: "[EXPERIMENTAL] show dependency origin tree of vulnerable packages",
|
||||
TelemetrySafe: true,
|
||||
Name: "dependency-tree",
|
||||
ConfigName: "dependency-tree",
|
||||
Usage: "[EXPERIMENTAL] show dependency origin tree of vulnerable packages",
|
||||
}
|
||||
ListAllPkgsFlag = Flag[bool]{
|
||||
Name: "list-all-pkgs",
|
||||
ConfigName: "list-all-pkgs",
|
||||
Usage: "output all packages in the JSON report regardless of vulnerability",
|
||||
TelemetrySafe: true,
|
||||
Name: "list-all-pkgs",
|
||||
ConfigName: "list-all-pkgs",
|
||||
Usage: "output all packages in the JSON report regardless of vulnerability",
|
||||
}
|
||||
IgnoreFileFlag = Flag[string]{
|
||||
Name: "ignorefile",
|
||||
@@ -75,16 +71,14 @@ var (
|
||||
Usage: "specify the Rego file path to evaluate each vulnerability",
|
||||
}
|
||||
ExitCodeFlag = Flag[int]{
|
||||
Name: "exit-code",
|
||||
ConfigName: "exit-code",
|
||||
Usage: "specify exit code when any security issues are found",
|
||||
TelemetrySafe: true,
|
||||
Name: "exit-code",
|
||||
ConfigName: "exit-code",
|
||||
Usage: "specify exit code when any security issues are found",
|
||||
}
|
||||
ExitOnEOLFlag = Flag[int]{
|
||||
Name: "exit-on-eol",
|
||||
ConfigName: "exit-on-eol",
|
||||
Usage: "exit with the specified code when the OS reaches end of service/life",
|
||||
TelemetrySafe: true,
|
||||
Name: "exit-on-eol",
|
||||
ConfigName: "exit-on-eol",
|
||||
Usage: "exit with the specified code when the OS reaches end of service/life",
|
||||
}
|
||||
OutputFlag = Flag[string]{
|
||||
Name: "output",
|
||||
@@ -98,13 +92,12 @@ var (
|
||||
Usage: "[EXPERIMENTAL] output plugin arguments",
|
||||
}
|
||||
SeverityFlag = Flag[[]string]{
|
||||
Name: "severity",
|
||||
ConfigName: "severity",
|
||||
Shorthand: "s",
|
||||
Default: dbTypes.SeverityNames,
|
||||
Values: dbTypes.SeverityNames,
|
||||
Usage: "severities of security issues to be displayed",
|
||||
TelemetrySafe: true,
|
||||
Name: "severity",
|
||||
ConfigName: "severity",
|
||||
Shorthand: "s",
|
||||
Default: dbTypes.SeverityNames,
|
||||
Values: dbTypes.SeverityNames,
|
||||
Usage: "severities of security issues to be displayed",
|
||||
}
|
||||
ComplianceFlag = Flag[string]{
|
||||
Name: "compliance",
|
||||
@@ -112,10 +105,9 @@ var (
|
||||
Usage: "compliance report to generate",
|
||||
}
|
||||
ShowSuppressedFlag = Flag[bool]{
|
||||
Name: "show-suppressed",
|
||||
ConfigName: "scan.show-suppressed",
|
||||
Usage: "[EXPERIMENTAL] show suppressed vulnerabilities",
|
||||
TelemetrySafe: true,
|
||||
Name: "show-suppressed",
|
||||
ConfigName: "scan.show-suppressed",
|
||||
Usage: "[EXPERIMENTAL] show suppressed vulnerabilities",
|
||||
}
|
||||
TableModeFlag = Flag[[]string]{
|
||||
Name: "table-mode",
|
||||
|
||||
@@ -27,10 +27,9 @@ var (
|
||||
Usage: "specify the files or glob patterns to skip",
|
||||
}
|
||||
OfflineScanFlag = Flag[bool]{
|
||||
Name: "offline-scan",
|
||||
ConfigName: "scan.offline",
|
||||
Usage: "do not issue API requests to identify dependencies",
|
||||
TelemetrySafe: true,
|
||||
Name: "offline-scan",
|
||||
ConfigName: "scan.offline",
|
||||
Usage: "do not issue API requests to identify dependencies",
|
||||
}
|
||||
ScannersFlag = Flag[[]string]{
|
||||
Name: "scanners",
|
||||
@@ -66,8 +65,7 @@ var (
|
||||
Deprecated: true, // --security-checks was renamed to --scanners
|
||||
},
|
||||
},
|
||||
Usage: "comma-separated list of what security issues to detect",
|
||||
TelemetrySafe: true,
|
||||
Usage: "comma-separated list of what security issues to detect",
|
||||
}
|
||||
FilePatternsFlag = Flag[[]string]{
|
||||
Name: "file-patterns",
|
||||
@@ -75,19 +73,17 @@ var (
|
||||
Usage: "specify config file patterns",
|
||||
}
|
||||
SlowFlag = Flag[bool]{
|
||||
Name: "slow",
|
||||
ConfigName: "scan.slow",
|
||||
Default: false,
|
||||
Usage: "scan over time with lower CPU and memory utilization",
|
||||
Deprecated: `Use "--parallel 1" instead.`,
|
||||
TelemetrySafe: true,
|
||||
Name: "slow",
|
||||
ConfigName: "scan.slow",
|
||||
Default: false,
|
||||
Usage: "scan over time with lower CPU and memory utilization",
|
||||
Deprecated: `Use "--parallel 1" instead.`,
|
||||
}
|
||||
ParallelFlag = Flag[int]{
|
||||
Name: "parallel",
|
||||
ConfigName: "scan.parallel",
|
||||
Default: 5,
|
||||
Usage: "number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism",
|
||||
TelemetrySafe: true,
|
||||
Name: "parallel",
|
||||
ConfigName: "scan.parallel",
|
||||
Default: 5,
|
||||
Usage: "number of goroutines enabled for parallel scanning, set 0 to auto-detect parallelism",
|
||||
}
|
||||
SBOMSourcesFlag = Flag[[]string]{
|
||||
Name: "sbom-sources",
|
||||
@@ -116,19 +112,16 @@ var (
|
||||
- "precise": Prioritizes precise by minimizing false positives.
|
||||
- "comprehensive": Aims to detect more security findings at the cost of potential false positives.
|
||||
`,
|
||||
TelemetrySafe: true,
|
||||
}
|
||||
DistroFlag = Flag[string]{
|
||||
Name: "distro",
|
||||
ConfigName: "scan.distro",
|
||||
Usage: "[EXPERIMENTAL] specify a distribution, <family>/<version>",
|
||||
TelemetrySafe: true,
|
||||
Name: "distro",
|
||||
ConfigName: "scan.distro",
|
||||
Usage: "[EXPERIMENTAL] specify a distribution, <family>/<version>",
|
||||
}
|
||||
SkipVersionCheckFlag = Flag[bool]{
|
||||
Name: "skip-version-check",
|
||||
ConfigName: "scan.skip-version-check",
|
||||
Usage: "suppress notices about version updates and Trivy announcements",
|
||||
TelemetrySafe: true,
|
||||
Name: "skip-version-check",
|
||||
ConfigName: "scan.skip-version-check",
|
||||
Usage: "suppress notices about version updates and Trivy announcements",
|
||||
}
|
||||
DisableTelemetryFlag = Flag[bool]{
|
||||
Name: "disable-telemetry",
|
||||
|
||||
@@ -12,17 +12,15 @@ import (
|
||||
|
||||
var (
|
||||
IgnoreUnfixedFlag = Flag[bool]{
|
||||
Name: "ignore-unfixed",
|
||||
ConfigName: "vulnerability.ignore-unfixed",
|
||||
Usage: "display only fixed vulnerabilities",
|
||||
TelemetrySafe: true,
|
||||
Name: "ignore-unfixed",
|
||||
ConfigName: "vulnerability.ignore-unfixed",
|
||||
Usage: "display only fixed vulnerabilities",
|
||||
}
|
||||
IgnoreStatusFlag = Flag[[]string]{
|
||||
Name: "ignore-status",
|
||||
ConfigName: "vulnerability.ignore-status",
|
||||
Values: dbTypes.Statuses,
|
||||
Usage: "comma-separated list of vulnerability status to ignore",
|
||||
TelemetrySafe: true,
|
||||
Name: "ignore-status",
|
||||
ConfigName: "vulnerability.ignore-status",
|
||||
Values: dbTypes.Statuses,
|
||||
Usage: "comma-separated list of vulnerability status to ignore",
|
||||
}
|
||||
VEXFlag = Flag[[]string]{
|
||||
Name: "vex",
|
||||
@@ -30,10 +28,9 @@ var (
|
||||
Usage: `[EXPERIMENTAL] VEX sources ("repo", "oci" or file path)`,
|
||||
}
|
||||
SkipVEXRepoUpdateFlag = Flag[bool]{
|
||||
Name: "skip-vex-repo-update",
|
||||
ConfigName: "vulnerability.skip-vex-repo-update",
|
||||
Usage: `[EXPERIMENTAL] Skip VEX Repository update`,
|
||||
TelemetrySafe: true,
|
||||
Name: "skip-vex-repo-update",
|
||||
ConfigName: "vulnerability.skip-vex-repo-update",
|
||||
Usage: `[EXPERIMENTAL] Skip VEX Repository update`,
|
||||
}
|
||||
VulnSeveritySourceFlag = Flag[[]string]{
|
||||
Name: "vuln-severity-source",
|
||||
@@ -41,9 +38,8 @@ var (
|
||||
Default: []string{
|
||||
"auto",
|
||||
},
|
||||
Values: append(xstrings.ToStringSlice(vulnerability.AllSourceIDs), "auto"),
|
||||
Usage: "order of data sources for selecting vulnerability severity level",
|
||||
TelemetrySafe: true,
|
||||
Values: append(xstrings.ToStringSlice(vulnerability.AllSourceIDs), "auto"),
|
||||
Usage: "order of data sources for selecting vulnerability severity level",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@ package iam
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
|
||||
"github.com/aquasecurity/iamgo"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/scan"
|
||||
@@ -17,18 +15,6 @@ type wrappedDocument struct {
|
||||
}
|
||||
|
||||
func ParsePolicyFromAttr(attr *terraform.Attribute, owner *terraform.Block, modules terraform.Modules) (*iam.Document, error) {
|
||||
if attr == nil {
|
||||
return &iam.Document{
|
||||
Metadata: owner.GetMetadata(),
|
||||
}, nil
|
||||
}
|
||||
attr.RewriteExpr(func(e hclsyntax.Expression) hclsyntax.Expression {
|
||||
if te, ok := e.(*hclsyntax.TemplateExpr); ok {
|
||||
return &terraform.PartialTemplateExpr{TemplateExpr: te}
|
||||
}
|
||||
return e
|
||||
})
|
||||
|
||||
if !attr.IsString() {
|
||||
return &iam.Document{
|
||||
Metadata: owner.GetMetadata(),
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package iam
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
|
||||
"github.com/aquasecurity/iamgo"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/providers/aws/iam"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/terraform"
|
||||
@@ -142,20 +140,11 @@ func findAttachmentPolicy(modules terraform.Modules) func(resource *terraform.Bl
|
||||
}
|
||||
}
|
||||
|
||||
// Searching for a referenced block only makes sense for traversal expressions,
|
||||
// since only they can directly reference other blocks in the configuration.
|
||||
switch attr.HCLAttribute().Expr.(type) {
|
||||
case *hclsyntax.RelativeTraversalExpr, *hclsyntax.ScopeTraversalExpr:
|
||||
if block, err := modules.GetReferencedBlock(attr, resource); err == nil {
|
||||
return findPolicy(modules)(block)
|
||||
}
|
||||
}
|
||||
return &iam.Policy{
|
||||
Metadata: resource.GetMetadata(),
|
||||
Document: iam.Document{
|
||||
Metadata: resource.GetMetadata(),
|
||||
},
|
||||
if block, err := modules.GetReferencedBlock(attr, resource); err == nil {
|
||||
return findPolicy(modules)(block)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -402,73 +402,6 @@ data "aws_iam_policy_document" "policy" {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "policy is template with unknown part",
|
||||
terraform: `variable "action" {
|
||||
default = null
|
||||
}
|
||||
|
||||
resource "aws_iam_policy" "test" {
|
||||
name = "test"
|
||||
|
||||
policy = <<EOF
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"${var.action}",
|
||||
"s3:get*",
|
||||
"s3:list*"
|
||||
],
|
||||
"Resource": "*"
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"kinesis:DescribeStream",
|
||||
"kinesis:GetRecords"
|
||||
],
|
||||
"Resource": [
|
||||
"${aws_kinesis_stream.stepfunction_ecs_kinesis_stream.arn}"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
}`,
|
||||
expected: []iam.Policy{
|
||||
{
|
||||
Name: iacTypes.StringTest("test"),
|
||||
Document: func() iam.Document {
|
||||
builder := iamgo.NewPolicyBuilder().
|
||||
WithStatement(
|
||||
iamgo.NewStatementBuilder().
|
||||
WithActions([]string{"__UNRESOLVED__", "s3:get*", "s3:list*"}).
|
||||
WithResources([]string{"*"}).
|
||||
WithEffect("Allow").
|
||||
Build(),
|
||||
).
|
||||
WithStatement(
|
||||
iamgo.NewStatementBuilder().
|
||||
WithActions([]string{"kinesis:DescribeStream", "kinesis:GetRecords"}).
|
||||
WithResources([]string{"__UNRESOLVED__"}).
|
||||
WithEffect("Allow").
|
||||
Build(),
|
||||
).
|
||||
WithVersion("2012-10-17")
|
||||
|
||||
return iam.Document{
|
||||
Parsed: builder.Build(),
|
||||
Metadata: iacTypes.NewTestMetadata(),
|
||||
IsOffset: false,
|
||||
HasRefs: true,
|
||||
}
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -338,32 +338,6 @@ resource "aws_iam_role_policy_attachment" "test" {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "policy is template with unknown part",
|
||||
terraform: `resource "aws_iam_role" "default" {
|
||||
name = "test"
|
||||
}
|
||||
|
||||
resource "aws_iam_role_policy_attachment" "amazon_eks_cluster_policy" {
|
||||
role = aws_iam_role.default.name
|
||||
policy_arn = format("arn:%s:iam::aws:policy/AmazonEKSClusterPolicy", data.aws_partition.current.partition)
|
||||
}
|
||||
|
||||
|
||||
data "aws_partition" "current" {}
|
||||
`,
|
||||
expected: []iam.Role{
|
||||
{
|
||||
Name: iacTypes.StringTest("test"),
|
||||
Policies: []iam.Policy{
|
||||
{
|
||||
Name: iacTypes.StringTest(""),
|
||||
Document: iam.Document{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
package rego
|
||||
|
||||
import (
|
||||
"github.com/open-policy-agent/opa/v1/ast"
|
||||
|
||||
"github.com/aquasecurity/go-version/pkg/semver"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/framework"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
)
|
||||
|
||||
type RegoModuleFilter func(module *ast.Module, metadata *StaticMetadata) bool
|
||||
|
||||
// TrivyVersionFilter returns a filter that allows only those modules,
|
||||
// that are compatible with the given version of Trivy.
|
||||
func TrivyVersionFilter(trivyVer string) RegoModuleFilter {
|
||||
if trivyVer == "dev" {
|
||||
return func(_ *ast.Module, _ *StaticMetadata) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
tv, tverr := semver.Parse(trivyVer)
|
||||
if tverr != nil {
|
||||
log.Warn(
|
||||
"Failed to parse Trivy version - cannot confirm if all modules will work with current version",
|
||||
log.Prefix("rego"),
|
||||
log.String("trivy_version", trivyVer),
|
||||
log.Err(tverr),
|
||||
)
|
||||
return func(_ *ast.Module, _ *StaticMetadata) bool {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return func(module *ast.Module, metadata *StaticMetadata) bool {
|
||||
return isMinimumVersionSupported(metadata, module, tv)
|
||||
}
|
||||
}
|
||||
|
||||
func isMinimumVersionSupported(metadata *StaticMetadata, module *ast.Module, tv semver.Version) bool {
|
||||
// to ensure compatibility with old modules without minimum trivy version
|
||||
if metadata.MinimumTrivyVersion == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
mmsv, err := semver.Parse(metadata.MinimumTrivyVersion)
|
||||
if err != nil {
|
||||
log.Warn(
|
||||
"Failed to parse minimum trivy version - skipping as cannot confirm if module will work with current version",
|
||||
log.Prefix("rego"),
|
||||
log.FilePath(module.Package.Location.File),
|
||||
log.Err(err),
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
if tv.LessThan(mmsv) {
|
||||
log.Warn(
|
||||
"Module will be skipped as current version of Trivy is older than minimum trivy version required - please update Trivy to use this module",
|
||||
log.Prefix("rego"),
|
||||
log.FilePath(module.Package.Location.File),
|
||||
log.String("minimum_trivy_version", metadata.MinimumTrivyVersion),
|
||||
)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// FrameworksFilter returns a filter that allows only modules
|
||||
// associated with the specified frameworks.
|
||||
func FrameworksFilter(frameworks []framework.Framework) RegoModuleFilter {
|
||||
return func(_ *ast.Module, metadata *StaticMetadata) bool {
|
||||
return metadata.matchAnyFramework(frameworks)
|
||||
}
|
||||
}
|
||||
|
||||
// IncludeDeprecatedFilter returns a filter that allows deprecated modules
|
||||
// if the include flag is true.
|
||||
func IncludeDeprecatedFilter(include bool) RegoModuleFilter {
|
||||
return func(_ *ast.Module, metadata *StaticMetadata) bool {
|
||||
if metadata.Deprecated && !include {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
package rego_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/open-policy-agent/opa/v1/ast"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/iac/framework"
|
||||
"github.com/aquasecurity/trivy/pkg/iac/rego"
|
||||
)
|
||||
|
||||
func TestTrivyVersionFilter(t *testing.T) {
|
||||
module := &ast.Module{Package: &ast.Package{Location: ast.NewLocation(nil, "/test.rego", 1, 1)}}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
trivyVer string
|
||||
minVersion string
|
||||
expected bool
|
||||
}{
|
||||
{"no minimum version", "0.1.0", "", true},
|
||||
{"compatible version", "0.20.0", "0.19.0", true},
|
||||
{"incompatible version", "0.18.0", "0.19.0", false},
|
||||
{"invalid min version", "0.20.0", "invalid", false},
|
||||
{"invalid trivy version", "invalid", "0.19.0", true},
|
||||
{"empty trivy version", "", "0.19.0", true},
|
||||
{"dev trivy version", "dev", "0.19.0", true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
filter := rego.TrivyVersionFilter(tt.trivyVer)
|
||||
metadata := ®o.StaticMetadata{
|
||||
MinimumTrivyVersion: tt.minVersion,
|
||||
}
|
||||
result := filter(module, metadata)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFrameworksFilter(t *testing.T) {
|
||||
module := &ast.Module{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
moduleFw map[framework.Framework][]string
|
||||
filterFw []framework.Framework
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "match single",
|
||||
moduleFw: map[framework.Framework][]string{
|
||||
framework.CIS_AWS_1_2: {"2.5"},
|
||||
},
|
||||
filterFw: []framework.Framework{framework.CIS_AWS_1_2},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "match one of many",
|
||||
moduleFw: map[framework.Framework][]string{
|
||||
framework.CIS_AWS_1_2: {"2.5"},
|
||||
framework.CIS_AWS_1_4: {"4.5"},
|
||||
},
|
||||
filterFw: []framework.Framework{framework.CIS_AWS_1_2},
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "no match",
|
||||
moduleFw: map[framework.Framework][]string{
|
||||
framework.CIS_AWS_1_2: {"2.5"},
|
||||
},
|
||||
filterFw: []framework.Framework{"PCI"},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "empty filter",
|
||||
moduleFw: map[framework.Framework][]string{
|
||||
framework.CIS_AWS_1_2: {"2.5"},
|
||||
},
|
||||
filterFw: nil,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "has default framework and empty filter",
|
||||
moduleFw: map[framework.Framework][]string{
|
||||
framework.Default: {},
|
||||
framework.CIS_AWS_1_2: {"2.5"},
|
||||
},
|
||||
filterFw: nil,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "empty module frameworks",
|
||||
moduleFw: nil,
|
||||
filterFw: []framework.Framework{framework.Default},
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
metadata := ®o.StaticMetadata{
|
||||
Frameworks: tt.moduleFw,
|
||||
}
|
||||
filter := rego.FrameworksFilter(tt.filterFw)
|
||||
result := filter(module, metadata)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIncludeDeprecatedFilter(t *testing.T) {
|
||||
module := &ast.Module{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
include bool
|
||||
deprecated bool
|
||||
expected bool
|
||||
}{
|
||||
{"include false, not deprecated", false, false, true},
|
||||
{"include false, deprecated", false, true, false},
|
||||
{"include true, deprecated", true, true, true},
|
||||
{"include true, not deprecated", true, false, true},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
metadata := ®o.StaticMetadata{Deprecated: tt.deprecated}
|
||||
filter := rego.IncludeDeprecatedFilter(tt.include)
|
||||
result := filter(module, metadata)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/open-policy-agent/opa/v1/bundle"
|
||||
"github.com/samber/lo"
|
||||
|
||||
"github.com/aquasecurity/go-version/pkg/semver"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/set"
|
||||
"github.com/aquasecurity/trivy/pkg/version/doc"
|
||||
@@ -296,6 +297,32 @@ func (s *Scanner) handleModulesMetadata(path string, module *ast.Module) {
|
||||
s.moduleMetadata[path] = metadata
|
||||
}
|
||||
|
||||
func (s *Scanner) IsMinimumVersionSupported(metadata *StaticMetadata, module *ast.Module, tv semver.Version) bool {
|
||||
if metadata.MinimumTrivyVersion == "" { // to ensure compatibility with old modules without minimum trivy version
|
||||
return true
|
||||
}
|
||||
|
||||
mmsv, err := semver.Parse(metadata.MinimumTrivyVersion)
|
||||
if err != nil {
|
||||
s.logger.Warn(
|
||||
"Failed to parse minimum trivy version - skipping as cannot confirm if module will work with current version",
|
||||
log.FilePath(module.Package.Location.File),
|
||||
log.Err(err),
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
if tv.LessThan(mmsv) {
|
||||
s.logger.Warn(
|
||||
"Module will be skipped as current version of Trivy is older than minimum trivy version required - please update Trivy to use this module",
|
||||
log.FilePath(module.Package.Location.File),
|
||||
log.String("minimum_trivy_version", metadata.MinimumTrivyVersion),
|
||||
)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// moduleHasLegacyMetadataFormat checks if the module has a legacy metadata format.
|
||||
// Returns true if the metadata is represented as a “__rego_metadata__” rule,
|
||||
// which was used before annotations were introduced.
|
||||
@@ -317,37 +344,52 @@ func moduleHasLegacyInputFormat(module *ast.Module) bool {
|
||||
// filterModules filters the Rego modules based on metadata.
|
||||
func (s *Scanner) filterModules() error {
|
||||
filtered := make(map[string]*ast.Module)
|
||||
tv, tverr := semver.Parse(s.trivyVersion)
|
||||
if tverr != nil && s.trivyVersion != "dev" {
|
||||
s.logger.Warn(
|
||||
"Failed to parse Trivy version - cannot confirm if all modules will work with current version",
|
||||
log.String("trivy_version", s.trivyVersion),
|
||||
log.Err(tverr),
|
||||
)
|
||||
}
|
||||
|
||||
for name, module := range s.policies {
|
||||
metadata, err := s.metadataForModule(context.Background(), name, module, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("retrieve metadata for module %s: %w", name, err)
|
||||
}
|
||||
if metadata == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if !lo.EveryBy(s.moduleFilters, func(filter RegoModuleFilter) bool {
|
||||
return filter(module, metadata)
|
||||
}) {
|
||||
continue
|
||||
}
|
||||
if metadata != nil {
|
||||
if tverr == nil && !s.IsMinimumVersionSupported(metadata, module, tv) {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(metadata.InputOptions.Selectors) == 0 && !metadata.Library {
|
||||
s.logger.Warn(
|
||||
"Module has no input selectors - it will be loaded for all inputs",
|
||||
log.FilePath(module.Package.Location.File),
|
||||
log.String("module", name),
|
||||
)
|
||||
if s.isModuleApplicable(module, metadata, name) {
|
||||
filtered[name] = module
|
||||
}
|
||||
}
|
||||
|
||||
filtered[name] = module
|
||||
}
|
||||
|
||||
s.policies = filtered
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Scanner) isModuleApplicable(module *ast.Module, metadata *StaticMetadata, name string) bool {
|
||||
if !metadata.hasAnyFramework(s.frameworks) {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(metadata.InputOptions.Selectors) == 0 && !metadata.Library {
|
||||
s.logger.Warn(
|
||||
"Module has no input selectors - it will be loaded for all inputs",
|
||||
log.FilePath(module.Package.Location.File),
|
||||
log.String("module", name),
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func ParseRegoModule(name, input string) (*ast.Module, error) {
|
||||
return ast.ParseModuleWithOpts(name, input, ast.ParserOptions{
|
||||
ProcessAnnotation: true,
|
||||
|
||||
@@ -179,7 +179,7 @@ func (sm *StaticMetadata) updateAliases(meta map[string]any) {
|
||||
}
|
||||
}
|
||||
|
||||
func (sm *StaticMetadata) matchAnyFramework(frameworks []framework.Framework) bool {
|
||||
func (sm *StaticMetadata) hasAnyFramework(frameworks []framework.Framework) bool {
|
||||
if len(frameworks) == 0 {
|
||||
frameworks = []framework.Framework{framework.Default}
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ func WithFrameworks(frameworks ...framework.Framework) options.ScannerOption {
|
||||
func WithTrivyVersion(version string) options.ScannerOption {
|
||||
return func(s options.ConfigurableScanner) {
|
||||
if ss, ok := s.(*Scanner); ok {
|
||||
ss.moduleFilters = append(ss.moduleFilters, TrivyVersionFilter(version))
|
||||
ss.trivyVersion = version
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/aquasecurity/trivy/pkg/iac/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/set"
|
||||
"github.com/aquasecurity/trivy/pkg/version/app"
|
||||
)
|
||||
|
||||
var checkTypesWithSubtype = set.New(types.SourceCloud, types.SourceDefsec, types.SourceKubernetes)
|
||||
@@ -61,8 +62,7 @@ type Scanner struct {
|
||||
includeDeprecatedChecks bool
|
||||
includeEmbeddedPolicies bool
|
||||
includeEmbeddedLibraries bool
|
||||
|
||||
moduleFilters []RegoModuleFilter
|
||||
trivyVersion string
|
||||
|
||||
embeddedLibs map[string]*ast.Module
|
||||
embeddedChecks map[string]*ast.Module
|
||||
@@ -98,18 +98,12 @@ func NewScanner(opts ...options.ScannerOption) *Scanner {
|
||||
logger: log.WithPrefix("rego"),
|
||||
customSchemas: make(map[string][]byte),
|
||||
moduleMetadata: make(map[string]*StaticMetadata),
|
||||
trivyVersion: app.Version(),
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(s)
|
||||
}
|
||||
|
||||
s.moduleFilters = append(
|
||||
s.moduleFilters,
|
||||
FrameworksFilter(s.frameworks),
|
||||
IncludeDeprecatedFilter(s.includeDeprecatedChecks),
|
||||
)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -212,6 +206,10 @@ func (s *Scanner) ScanInput(ctx context.Context, sourceType types.Source, inputs
|
||||
continue
|
||||
}
|
||||
|
||||
if !s.includeDeprecatedChecks && staticMeta.Deprecated {
|
||||
continue // skip deprecated checks
|
||||
}
|
||||
|
||||
// skip if check isn't relevant to what is being scanned
|
||||
if !isPolicyApplicable(sourceType, staticMeta, inputs...) {
|
||||
continue
|
||||
|
||||
@@ -12,12 +12,10 @@ import (
|
||||
"github.com/hashicorp/hcl/v2/ext/typeexpr"
|
||||
"github.com/hashicorp/hcl/v2/hclsyntax"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
"github.com/zclconf/go-cty/cty/convert"
|
||||
"github.com/zclconf/go-cty/cty/gocty"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/iac/terraform/context"
|
||||
iacTypes "github.com/aquasecurity/trivy/pkg/iac/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
)
|
||||
|
||||
type Attribute struct {
|
||||
@@ -830,120 +828,3 @@ func safeOp[T any](a *Attribute, fn func(cty.Value) T) T {
|
||||
|
||||
return fn(val)
|
||||
}
|
||||
|
||||
// RewriteExpr applies the given function `transform` to the expression of the attribute,
|
||||
// recursively traversing and transforming it.
|
||||
func (a *Attribute) RewriteExpr(transform func(hclsyntax.Expression) hclsyntax.Expression) {
|
||||
if a == nil || a.hclAttribute == nil {
|
||||
return
|
||||
}
|
||||
expr, ok := a.hclAttribute.Expr.(hclsyntax.Expression)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
a.hclAttribute.Expr = RewriteExpr(expr, transform)
|
||||
}
|
||||
|
||||
// nolint: gocyclo
|
||||
// RewriteExpr recursively rewrites an HCL expression tree in-place,
|
||||
// applying the provided transformation function `transform` to each node.
|
||||
func RewriteExpr(
|
||||
expr hclsyntax.Expression,
|
||||
transform func(hclsyntax.Expression) hclsyntax.Expression,
|
||||
) hclsyntax.Expression {
|
||||
if expr == nil {
|
||||
return nil
|
||||
}
|
||||
switch e := expr.(type) {
|
||||
case *hclsyntax.LiteralValueExpr:
|
||||
case *hclsyntax.TemplateExpr:
|
||||
for i, p := range e.Parts {
|
||||
e.Parts[i] = RewriteExpr(p, transform)
|
||||
}
|
||||
case *hclsyntax.TemplateWrapExpr:
|
||||
e.Wrapped = RewriteExpr(e.Wrapped, transform)
|
||||
case *hclsyntax.BinaryOpExpr:
|
||||
e.LHS = RewriteExpr(e.LHS, transform)
|
||||
e.RHS = RewriteExpr(e.RHS, transform)
|
||||
case *hclsyntax.UnaryOpExpr:
|
||||
e.Val = RewriteExpr(e.Val, transform)
|
||||
case *hclsyntax.TupleConsExpr:
|
||||
for i, elem := range e.Exprs {
|
||||
e.Exprs[i] = RewriteExpr(elem, transform)
|
||||
}
|
||||
case *hclsyntax.ParenthesesExpr:
|
||||
e.Expression = RewriteExpr(e.Expression, transform)
|
||||
case *hclsyntax.ObjectConsExpr:
|
||||
for i, item := range e.Items {
|
||||
e.Items[i].KeyExpr = RewriteExpr(item.KeyExpr, transform)
|
||||
e.Items[i].ValueExpr = RewriteExpr(item.ValueExpr, transform)
|
||||
}
|
||||
case *hclsyntax.ObjectConsKeyExpr:
|
||||
e.Wrapped = RewriteExpr(e.Wrapped, transform)
|
||||
case *hclsyntax.ScopeTraversalExpr:
|
||||
case *hclsyntax.RelativeTraversalExpr:
|
||||
e.Source = RewriteExpr(e.Source, transform)
|
||||
case *hclsyntax.ConditionalExpr:
|
||||
e.Condition = RewriteExpr(e.Condition, transform)
|
||||
e.TrueResult = RewriteExpr(e.TrueResult, transform)
|
||||
e.FalseResult = RewriteExpr(e.FalseResult, transform)
|
||||
case *hclsyntax.FunctionCallExpr:
|
||||
for i, arg := range e.Args {
|
||||
e.Args[i] = RewriteExpr(arg, transform)
|
||||
}
|
||||
case *hclsyntax.IndexExpr:
|
||||
e.Collection = RewriteExpr(e.Collection, transform)
|
||||
e.Key = RewriteExpr(e.Key, transform)
|
||||
case *hclsyntax.ForExpr:
|
||||
e.CollExpr = RewriteExpr(e.CollExpr, transform)
|
||||
e.KeyExpr = RewriteExpr(e.KeyExpr, transform)
|
||||
e.ValExpr = RewriteExpr(e.ValExpr, transform)
|
||||
e.CondExpr = RewriteExpr(e.CondExpr, transform)
|
||||
case *hclsyntax.SplatExpr:
|
||||
e.Source = RewriteExpr(e.Source, transform)
|
||||
case *hclsyntax.AnonSymbolExpr:
|
||||
default:
|
||||
log.Debug(
|
||||
"RewriteExpr encountered an unhandled expression type",
|
||||
log.Prefix(log.PrefixMisconfiguration),
|
||||
log.String("expr_type", fmt.Sprintf("%T", expr)),
|
||||
)
|
||||
}
|
||||
return transform(expr)
|
||||
}
|
||||
|
||||
// UnknownValuePrefix is a placeholder string used to represent parts of a
|
||||
// template expression that cannot be fully evaluated due to unknown values.
|
||||
const UnknownValuePrefix = "__UNRESOLVED__"
|
||||
|
||||
// PartialTemplateExpr is a wrapper around hclsyntax.TemplateExpr that
|
||||
// replaces unknown or unevaluated parts with placeholder strings during evaluation.
|
||||
type PartialTemplateExpr struct {
|
||||
*hclsyntax.TemplateExpr
|
||||
}
|
||||
|
||||
func (e *PartialTemplateExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
||||
parts := make([]hclsyntax.Expression, len(e.Parts))
|
||||
for i, part := range e.Parts {
|
||||
partVal, diags := part.Value(ctx)
|
||||
if diags.HasErrors() || partVal.IsNull() || !partVal.IsKnown() {
|
||||
parts[i] = &hclsyntax.LiteralValueExpr{
|
||||
Val: cty.StringVal(UnknownValuePrefix),
|
||||
SrcRange: part.Range(),
|
||||
}
|
||||
} else if _, err := convert.Convert(partVal, cty.String); err != nil {
|
||||
parts[i] = &hclsyntax.LiteralValueExpr{
|
||||
Val: cty.StringVal(UnknownValuePrefix),
|
||||
SrcRange: part.Range(),
|
||||
}
|
||||
} else {
|
||||
parts[i] = part
|
||||
}
|
||||
}
|
||||
newTemplate := &hclsyntax.TemplateExpr{
|
||||
Parts: parts,
|
||||
SrcRange: e.SrcRange,
|
||||
}
|
||||
|
||||
return newTemplate.Value(ctx)
|
||||
}
|
||||
|
||||
@@ -8,23 +8,20 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/samber/lo"
|
||||
|
||||
"github.com/aquasecurity/go-version/pkg/semver"
|
||||
"github.com/aquasecurity/trivy/pkg/flag"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/version/app"
|
||||
xhttp "github.com/aquasecurity/trivy/pkg/x/http"
|
||||
)
|
||||
|
||||
type VersionChecker struct {
|
||||
updatesApi string
|
||||
commandName string
|
||||
cliOptions *flag.Options
|
||||
updatesApi string
|
||||
skipUpdateCheck bool
|
||||
quiet bool
|
||||
telemetryDisabled bool
|
||||
|
||||
done bool
|
||||
responseReceived bool
|
||||
@@ -33,15 +30,17 @@ type VersionChecker struct {
|
||||
}
|
||||
|
||||
// NewVersionChecker creates a new VersionChecker with the default
|
||||
// updates API URL.
|
||||
func NewVersionChecker(commandName string, cliOptions *flag.Options) *VersionChecker {
|
||||
// updates API URL. The URL can be overridden by passing an Option
|
||||
// to the NewVersionChecker function.
|
||||
func NewVersionChecker(opts ...Option) *VersionChecker {
|
||||
v := &VersionChecker{
|
||||
updatesApi: "https://check.trivy.dev/updates",
|
||||
currentVersion: app.Version(),
|
||||
commandName: commandName,
|
||||
cliOptions: cliOptions,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(v)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
@@ -51,17 +50,17 @@ func NewVersionChecker(commandName string, cliOptions *flag.Options) *VersionChe
|
||||
// 1. if skipUpdateCheck is true AND telemetryDisabled are both true, skip the request
|
||||
// 2. if skipUpdateCheck is true AND telemetryDisabled is false, run check with metric details but suppress output
|
||||
// 3. if skipUpdateCheck is false AND telemetryDisabled is true, run update check but don't send any metric identifiers
|
||||
func (v *VersionChecker) RunUpdateCheck(ctx context.Context) {
|
||||
func (v *VersionChecker) RunUpdateCheck(ctx context.Context, args []string) {
|
||||
logger := log.WithPrefix("notification")
|
||||
|
||||
if v.cliOptions.SkipVersionCheck && v.cliOptions.DisableTelemetry {
|
||||
if v.skipUpdateCheck && v.telemetryDisabled {
|
||||
logger.Debug("Skipping update check and metric ping")
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
logger.Debug("Running version check")
|
||||
commandParts := v.getFlags()
|
||||
args = getFlags(args)
|
||||
client := xhttp.ClientWithContext(ctx, xhttp.WithTimeout(3*time.Second))
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, v.updatesApi, http.NoBody)
|
||||
@@ -71,10 +70,9 @@ func (v *VersionChecker) RunUpdateCheck(ctx context.Context) {
|
||||
}
|
||||
|
||||
// if the user hasn't disabled metrics, send the anonymous information as headers
|
||||
if !v.cliOptions.DisableTelemetry {
|
||||
if !v.telemetryDisabled {
|
||||
req.Header.Set("Trivy-Identifier", uniqueIdentifier())
|
||||
req.Header.Set("Trivy-Command", v.commandName)
|
||||
req.Header.Set("Trivy-Flags", commandParts)
|
||||
req.Header.Set("Trivy-Command", strings.Join(args, " "))
|
||||
req.Header.Set("Trivy-OS", runtime.GOOS)
|
||||
req.Header.Set("Trivy-Arch", runtime.GOARCH)
|
||||
}
|
||||
@@ -93,7 +91,7 @@ func (v *VersionChecker) RunUpdateCheck(ctx context.Context) {
|
||||
}
|
||||
|
||||
// enable priting if update allowed and quiet mode is not set
|
||||
if !v.cliOptions.SkipVersionCheck && !v.cliOptions.Quiet {
|
||||
if !v.skipUpdateCheck && !v.quiet {
|
||||
v.responseReceived = true
|
||||
}
|
||||
logger.Debug("Version check completed", log.String("latest_version", v.latestVersion.Trivy.LatestVersion))
|
||||
@@ -177,6 +175,17 @@ func (v *VersionChecker) Warnings() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// getFlags returns the just the flag portion without the values
|
||||
func getFlags(args []string) []string {
|
||||
var flags []string
|
||||
for _, arg := range args {
|
||||
if strings.HasPrefix(arg, "-") {
|
||||
flags = append(flags, strings.Split(arg, "=")[0])
|
||||
}
|
||||
}
|
||||
return flags
|
||||
}
|
||||
|
||||
func (fd *flexibleTime) UnmarshalJSON(b []byte) error {
|
||||
s := strings.Trim(string(b), `"`)
|
||||
if s == "" {
|
||||
@@ -202,39 +211,3 @@ func (fd *flexibleTime) UnmarshalJSON(b []byte) error {
|
||||
|
||||
return fmt.Errorf("unable to parse date: %s", s)
|
||||
}
|
||||
|
||||
func (v *VersionChecker) getFlags() string {
|
||||
var flags []string
|
||||
for _, f := range v.cliOptions.GetUsedFlags() {
|
||||
name := f.GetName()
|
||||
if name == "" {
|
||||
continue // Skip flags without a name
|
||||
}
|
||||
value := lo.Ternary(!f.IsTelemetrySafe(), "***", getFlagValue(f))
|
||||
|
||||
flags = append(flags, fmt.Sprintf("--%s=%s", name, value))
|
||||
}
|
||||
return strings.Join(flags, " ")
|
||||
}
|
||||
|
||||
func getFlagValue(f flag.Flagger) string {
|
||||
type flagger[T flag.FlagType] interface {
|
||||
Value() T
|
||||
}
|
||||
switch ff := f.(type) {
|
||||
case flagger[string]:
|
||||
return ff.Value()
|
||||
case flagger[int]:
|
||||
return strconv.Itoa(ff.Value())
|
||||
case flagger[float64]:
|
||||
return fmt.Sprintf("%f", ff.Value())
|
||||
case flagger[bool]:
|
||||
return strconv.FormatBool(ff.Value())
|
||||
case flagger[time.Duration]:
|
||||
return ff.Value().String()
|
||||
case flagger[[]string]:
|
||||
return strings.Join(ff.Value(), ",")
|
||||
default:
|
||||
return "***" // Default case for unsupported types
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,21 +9,14 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/flag"
|
||||
)
|
||||
|
||||
func TestPrintNotices(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
skipVersionCheck bool
|
||||
quiet bool
|
||||
disableTelemetry bool
|
||||
|
||||
currentVersion string
|
||||
options []Option
|
||||
latestVersion string
|
||||
announcements []announcement
|
||||
responseExpected bool
|
||||
@@ -31,38 +24,42 @@ func TestPrintNotices(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "New version with no announcements",
|
||||
currentVersion: "0.58.0",
|
||||
options: []Option{WithCurrentVersion("0.58.0")},
|
||||
latestVersion: "0.60.0",
|
||||
responseExpected: true,
|
||||
expectedOutput: "\n📣 \x1b[34mNotices:\x1b[0m\n - Version 0.60.0 of Trivy is now available, current version is 0.58.0\n\nTo suppress version checks, run Trivy scans with the --skip-version-check flag\n\n",
|
||||
},
|
||||
{
|
||||
name: "New version available but includes a prefixed version number",
|
||||
currentVersion: "0.58.0",
|
||||
options: []Option{WithCurrentVersion("0.58.0")},
|
||||
latestVersion: "v0.60.0",
|
||||
responseExpected: true,
|
||||
expectedOutput: "\n📣 \x1b[34mNotices:\x1b[0m\n - Version 0.60.0 of Trivy is now available, current version is 0.58.0\n\nTo suppress version checks, run Trivy scans with the --skip-version-check flag\n\n",
|
||||
},
|
||||
{
|
||||
name: "new version available but --quiet mode enabled",
|
||||
quiet: true,
|
||||
currentVersion: "0.58.0",
|
||||
name: "new version available but --quiet mode enabled",
|
||||
options: []Option{
|
||||
WithCurrentVersion("0.58.0"),
|
||||
WithQuietMode(true),
|
||||
},
|
||||
latestVersion: "0.60.0",
|
||||
responseExpected: false,
|
||||
expectedOutput: "",
|
||||
},
|
||||
{
|
||||
name: "new version available but --skip-version-check mode enabled",
|
||||
skipVersionCheck: true,
|
||||
currentVersion: "0.58.0",
|
||||
name: "new version available but --skip-update-check mode enabled",
|
||||
options: []Option{
|
||||
WithCurrentVersion("0.58.0"),
|
||||
WithSkipVersionCheck(true),
|
||||
},
|
||||
latestVersion: "0.60.0",
|
||||
responseExpected: false,
|
||||
expectedOutput: "",
|
||||
},
|
||||
{
|
||||
name: "New version with announcements",
|
||||
currentVersion: "0.58.0",
|
||||
latestVersion: "0.60.0",
|
||||
name: "New version with announcements",
|
||||
options: []Option{WithCurrentVersion("0.58.0")},
|
||||
latestVersion: "0.60.0",
|
||||
announcements: []announcement{
|
||||
{
|
||||
FromDate: time.Date(2025, 2, 2, 12, 0, 0, 0, time.UTC),
|
||||
@@ -74,9 +71,9 @@ func TestPrintNotices(t *testing.T) {
|
||||
expectedOutput: "\n📣 \x1b[34mNotices:\x1b[0m\n - There are some amazing things happening right now!\n - Version 0.60.0 of Trivy is now available, current version is 0.58.0\n\nTo suppress version checks, run Trivy scans with the --skip-version-check flag\n\n",
|
||||
},
|
||||
{
|
||||
name: "No new version with announcements",
|
||||
currentVersion: "0.60.0",
|
||||
latestVersion: "0.60.0",
|
||||
name: "No new version with announcements",
|
||||
options: []Option{WithCurrentVersion("0.60.0")},
|
||||
latestVersion: "0.60.0",
|
||||
announcements: []announcement{
|
||||
{
|
||||
FromDate: time.Date(2025, 2, 2, 12, 0, 0, 0, time.UTC),
|
||||
@@ -88,9 +85,9 @@ func TestPrintNotices(t *testing.T) {
|
||||
expectedOutput: "\n📣 \x1b[34mNotices:\x1b[0m\n - There are some amazing things happening right now!\n\nTo suppress version checks, run Trivy scans with the --skip-version-check flag\n\n",
|
||||
},
|
||||
{
|
||||
name: "No new version with announcements and zero time",
|
||||
currentVersion: "0.60.0",
|
||||
latestVersion: "0.60.0",
|
||||
name: "No new version with announcements and zero time",
|
||||
options: []Option{WithCurrentVersion("0.60.0")},
|
||||
latestVersion: "0.60.0",
|
||||
announcements: []announcement{
|
||||
{
|
||||
FromDate: time.Time{},
|
||||
@@ -102,9 +99,9 @@ func TestPrintNotices(t *testing.T) {
|
||||
expectedOutput: "\n📣 \x1b[34mNotices:\x1b[0m\n - There are some amazing things happening right now!\n\nTo suppress version checks, run Trivy scans with the --skip-version-check flag\n\n",
|
||||
},
|
||||
{
|
||||
name: "No new version with announcement that fails announcement version constraints",
|
||||
currentVersion: "0.60.0",
|
||||
latestVersion: "0.60.0",
|
||||
name: "No new version with announcement that fails announcement version constraints",
|
||||
options: []Option{WithCurrentVersion("0.60.0")},
|
||||
latestVersion: "0.60.0",
|
||||
announcements: []announcement{
|
||||
{
|
||||
FromDate: time.Date(2025, 2, 2, 12, 0, 0, 0, time.UTC),
|
||||
@@ -117,9 +114,9 @@ func TestPrintNotices(t *testing.T) {
|
||||
expectedOutput: "",
|
||||
},
|
||||
{
|
||||
name: "No new version with announcement where current version is greater than to_version",
|
||||
currentVersion: "0.60.0",
|
||||
latestVersion: "0.60.0",
|
||||
name: "No new version with announcement where current version is greater than to_version",
|
||||
options: []Option{WithCurrentVersion("0.60.0")},
|
||||
latestVersion: "0.60.0",
|
||||
announcements: []announcement{
|
||||
{
|
||||
FromDate: time.Date(2025, 2, 2, 12, 0, 0, 0, time.UTC),
|
||||
@@ -132,9 +129,9 @@ func TestPrintNotices(t *testing.T) {
|
||||
expectedOutput: "",
|
||||
},
|
||||
{
|
||||
name: "No new version with announcement that satisfies version constraint but outside date range",
|
||||
currentVersion: "0.60.0",
|
||||
latestVersion: "0.60.0",
|
||||
name: "No new version with announcement that satisfies version constraint but outside date range",
|
||||
options: []Option{WithCurrentVersion("0.60.0")},
|
||||
latestVersion: "0.60.0",
|
||||
announcements: []announcement{
|
||||
{
|
||||
FromDate: time.Date(2024, 2, 2, 12, 0, 0, 0, time.UTC),
|
||||
@@ -147,9 +144,9 @@ func TestPrintNotices(t *testing.T) {
|
||||
expectedOutput: "",
|
||||
},
|
||||
{
|
||||
name: "No new version with multiple announcements, one of which is valid",
|
||||
currentVersion: "0.60.0",
|
||||
latestVersion: "0.60.0",
|
||||
name: "No new version with multiple announcements, one of which is valid",
|
||||
options: []Option{WithCurrentVersion("0.60.0")},
|
||||
latestVersion: "0.60.0",
|
||||
announcements: []announcement{
|
||||
{
|
||||
FromDate: time.Date(2025, 2, 2, 12, 0, 0, 0, time.UTC),
|
||||
@@ -168,8 +165,7 @@ func TestPrintNotices(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "No new version with no announcements and quiet mode",
|
||||
quiet: true,
|
||||
currentVersion: "0.60.0",
|
||||
options: []Option{WithCurrentVersion("0.60.0"), WithQuietMode(true)},
|
||||
latestVersion: "0.60.0",
|
||||
announcements: []announcement{},
|
||||
responseExpected: false,
|
||||
@@ -177,7 +173,7 @@ func TestPrintNotices(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "No new version with no announcements",
|
||||
currentVersion: "0.60.0",
|
||||
options: []Option{WithCurrentVersion("0.60.0")},
|
||||
latestVersion: "0.60.0",
|
||||
announcements: []announcement{},
|
||||
responseExpected: true,
|
||||
@@ -189,22 +185,11 @@ func TestPrintNotices(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
updates := newUpdatesServer(t, tt.latestVersion, tt.announcements)
|
||||
server := httptest.NewServer(http.HandlerFunc(updates.handler))
|
||||
defer server.Close()
|
||||
tt.options = append(tt.options, WithUpdatesApi(server.URL))
|
||||
v := NewVersionChecker(tt.options...)
|
||||
|
||||
cliOpts := &flag.Options{
|
||||
GlobalOptions: flag.GlobalOptions{
|
||||
Quiet: tt.quiet,
|
||||
},
|
||||
ScanOptions: flag.ScanOptions{
|
||||
SkipVersionCheck: tt.skipVersionCheck,
|
||||
DisableTelemetry: tt.disableTelemetry,
|
||||
},
|
||||
}
|
||||
|
||||
v := NewVersionChecker("testCommand", cliOpts)
|
||||
v.updatesApi = server.URL
|
||||
v.currentVersion = tt.currentVersion
|
||||
|
||||
v.RunUpdateCheck(t.Context())
|
||||
v.RunUpdateCheck(t.Context(), nil)
|
||||
require.Eventually(t, func() bool { return v.done }, time.Second*5, 500)
|
||||
require.Eventually(t, func() bool { return v.responseReceived == tt.responseExpected }, time.Second*5, 500)
|
||||
|
||||
@@ -222,29 +207,32 @@ func TestPrintNotices(t *testing.T) {
|
||||
func TestCheckForNotices(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
skipVersionCheck bool
|
||||
disableTelemetry bool
|
||||
quiet bool
|
||||
currentVersion string
|
||||
options []Option
|
||||
expectedVersion string
|
||||
expectedAnnouncements []announcement
|
||||
expectNoMetrics bool
|
||||
}{
|
||||
{
|
||||
name: "new version with no announcements",
|
||||
currentVersion: "0.58.0",
|
||||
name: "new version with no announcements",
|
||||
options: []Option{
|
||||
WithCurrentVersion("0.58.0"),
|
||||
},
|
||||
expectedVersion: "0.60.0",
|
||||
},
|
||||
{
|
||||
name: "new version with disabled metrics",
|
||||
disableTelemetry: true,
|
||||
currentVersion: "0.58.0",
|
||||
expectedVersion: "0.60.0",
|
||||
expectNoMetrics: true,
|
||||
name: "new version with disabled metrics",
|
||||
options: []Option{
|
||||
WithCurrentVersion("0.58.0"),
|
||||
WithTelemetryDisabled(true),
|
||||
},
|
||||
expectedVersion: "0.60.0",
|
||||
expectNoMetrics: true,
|
||||
},
|
||||
{
|
||||
name: "new version and a new announcement",
|
||||
currentVersion: "0.58.0",
|
||||
name: "new version and a new announcement",
|
||||
options: []Option{
|
||||
WithCurrentVersion("0.58.0"),
|
||||
},
|
||||
expectedVersion: "0.60.0",
|
||||
expectedAnnouncements: []announcement{
|
||||
{
|
||||
@@ -262,20 +250,10 @@ func TestCheckForNotices(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(updates.handler))
|
||||
defer server.Close()
|
||||
|
||||
cliOpts := &flag.Options{
|
||||
GlobalOptions: flag.GlobalOptions{
|
||||
Quiet: tt.quiet,
|
||||
},
|
||||
ScanOptions: flag.ScanOptions{
|
||||
SkipVersionCheck: tt.skipVersionCheck,
|
||||
DisableTelemetry: tt.disableTelemetry,
|
||||
},
|
||||
}
|
||||
tt.options = append(tt.options, WithUpdatesApi(server.URL))
|
||||
v := NewVersionChecker(tt.options...)
|
||||
|
||||
v := NewVersionChecker("testCommand", cliOpts)
|
||||
v.updatesApi = server.URL
|
||||
|
||||
v.RunUpdateCheck(t.Context())
|
||||
v.RunUpdateCheck(t.Context(), nil)
|
||||
require.Eventually(t, func() bool { return v.done }, time.Second*5, 500)
|
||||
require.Eventually(t, func() bool { return v.responseReceived }, time.Second*5, 500)
|
||||
latestVersion, err := v.LatestVersion()
|
||||
@@ -284,9 +262,11 @@ func TestCheckForNotices(t *testing.T) {
|
||||
assert.ElementsMatch(t, tt.expectedAnnouncements, v.Announcements())
|
||||
|
||||
if tt.expectNoMetrics {
|
||||
assert.True(t, v.telemetryDisabled)
|
||||
require.NotNil(t, updates.lastRequest)
|
||||
assert.Empty(t, updates.lastRequest.Header.Get("Trivy-Identifier"))
|
||||
} else {
|
||||
assert.False(t, v.telemetryDisabled)
|
||||
require.NotNil(t, updates.lastRequest)
|
||||
assert.NotEmpty(t, updates.lastRequest.Header.Get("Trivy-Identifier"))
|
||||
}
|
||||
@@ -364,116 +344,3 @@ func TestFlexibleDate(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckCommandHeaders(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
command string
|
||||
commandArgs []string
|
||||
env map[string]string
|
||||
ignoreParseError bool
|
||||
expectedCommandHeader string
|
||||
expectedCommandArgsHeader string
|
||||
}{
|
||||
{
|
||||
name: "image command with no flags",
|
||||
command: "image",
|
||||
commandArgs: []string{"nginx"},
|
||||
expectedCommandHeader: "image",
|
||||
},
|
||||
{
|
||||
name: "image command with flags",
|
||||
command: "image",
|
||||
commandArgs: []string{"--severity", "CRITICAL", "--scanners", "vuln,misconfig", "--pkg-types", "library", "nginx", "--include-dev-deps"},
|
||||
expectedCommandHeader: "image",
|
||||
expectedCommandArgsHeader: "--include-dev-deps=true --pkg-types=library --severity=CRITICAL --scanners=vuln,misconfig",
|
||||
},
|
||||
{
|
||||
name: "image command with multiple flags",
|
||||
command: "image",
|
||||
commandArgs: []string{"--severity", "MEDIUM", "-s", "CRITICAL", "--scanners", "misconfig", "nginx"},
|
||||
expectedCommandHeader: "image",
|
||||
expectedCommandArgsHeader: "--severity=MEDIUM,CRITICAL --scanners=misconfig",
|
||||
},
|
||||
{
|
||||
name: "filesystem command with flags",
|
||||
command: "fs",
|
||||
commandArgs: []string{"--severity=HIGH", "--vex", "repo", "--vuln-severity-source", "nvd,debian", "../trivy-ci-test"},
|
||||
expectedCommandHeader: "fs",
|
||||
expectedCommandArgsHeader: "--severity=HIGH --vex=*** --vuln-severity-source=nvd,debian",
|
||||
},
|
||||
{
|
||||
name: "filesystem command with flags including an invalid flag",
|
||||
command: "fs",
|
||||
commandArgs: []string{"--severity=HIGH", "--vex", "repo", "--vuln-severity-source", "nvd,debian", "--invalid-flag", "../trivy-ci-test"},
|
||||
ignoreParseError: true,
|
||||
expectedCommandHeader: "fs",
|
||||
expectedCommandArgsHeader: "--severity=HIGH --vex=*** --vuln-severity-source=nvd,debian",
|
||||
},
|
||||
{
|
||||
name: "filesystem with environment variables",
|
||||
command: "fs",
|
||||
commandArgs: []string{"--severity", "HIGH", "--vex", "repo", "/home/user/code"},
|
||||
env: map[string]string{
|
||||
"TRIVY_SCANNERS": "secret,misconfig",
|
||||
},
|
||||
expectedCommandHeader: "fs",
|
||||
expectedCommandArgsHeader: "--severity=HIGH --scanners=secret,misconfig --vex=***",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
updates := newUpdatesServer(t, "0.60.0", nil)
|
||||
server := httptest.NewServer(http.HandlerFunc(updates.handler))
|
||||
defer server.Close()
|
||||
|
||||
for key, value := range tt.env {
|
||||
t.Setenv(key, value)
|
||||
}
|
||||
|
||||
// clean up the env
|
||||
defer func() {
|
||||
server.Close()
|
||||
}()
|
||||
|
||||
opts := getOptionsForArgs(t, tt.commandArgs, tt.ignoreParseError)
|
||||
|
||||
v := NewVersionChecker(tt.command, opts)
|
||||
v.updatesApi = server.URL
|
||||
v.RunUpdateCheck(t.Context())
|
||||
|
||||
require.Eventually(t, func() bool { return v.done }, time.Second*5, 500)
|
||||
require.NotNil(t, updates.lastRequest)
|
||||
assert.Equal(t, tt.expectedCommandHeader, updates.lastRequest.Header.Get("Trivy-Command"))
|
||||
assert.Equal(t, tt.expectedCommandArgsHeader, updates.lastRequest.Header.Get("Trivy-Flags"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// getOptionsForArgs uses a basic command to parse the flags so we can generate
|
||||
// an options object from it
|
||||
func getOptionsForArgs(t *testing.T, commandArgs []string, ignoreParseError bool) *flag.Options {
|
||||
flags := flag.Flags{
|
||||
flag.NewGlobalFlagGroup(),
|
||||
flag.NewImageFlagGroup(),
|
||||
flag.NewMisconfFlagGroup(),
|
||||
flag.NewPackageFlagGroup(),
|
||||
flag.NewReportFlagGroup(),
|
||||
flag.NewScanFlagGroup(),
|
||||
flag.NewVulnerabilityFlagGroup(),
|
||||
}
|
||||
|
||||
// simple command to facilitate flag parsing
|
||||
cmd := &cobra.Command{}
|
||||
flags.AddFlags(cmd)
|
||||
err := cmd.ParseFlags(commandArgs)
|
||||
if !ignoreParseError {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
require.NoError(t, flags.Bind(cmd))
|
||||
opts, err := flags.ToOptions(commandArgs)
|
||||
require.NoError(t, err)
|
||||
return &opts
|
||||
}
|
||||
|
||||
37
pkg/notification/option.go
Normal file
37
pkg/notification/option.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package notification
|
||||
|
||||
type Option func(*VersionChecker)
|
||||
|
||||
// WithUpdatesApi sets the updates API URL
|
||||
func WithUpdatesApi(updatesApi string) Option {
|
||||
return func(v *VersionChecker) {
|
||||
v.updatesApi = updatesApi
|
||||
}
|
||||
}
|
||||
|
||||
// WithCurrentVersion sets the current version
|
||||
func WithCurrentVersion(version string) Option {
|
||||
return func(v *VersionChecker) {
|
||||
v.currentVersion = version
|
||||
}
|
||||
}
|
||||
|
||||
func WithSkipVersionCheck(skipVersionCheck bool) Option {
|
||||
return func(v *VersionChecker) {
|
||||
v.skipUpdateCheck = skipVersionCheck
|
||||
}
|
||||
}
|
||||
|
||||
// WithQuietMode sets the quiet mode when the user is using the --quiet flag
|
||||
func WithQuietMode(quiet bool) Option {
|
||||
return func(v *VersionChecker) {
|
||||
v.quiet = quiet
|
||||
}
|
||||
}
|
||||
|
||||
// WithTelemetryDisabled sets the telemetry disabled flag
|
||||
func WithTelemetryDisabled(telemetryDisabled bool) Option {
|
||||
return func(v *VersionChecker) {
|
||||
v.telemetryDisabled = telemetryDisabled
|
||||
}
|
||||
}
|
||||
@@ -26,10 +26,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
ToolVendor = "aquasecurity"
|
||||
ToolName = "trivy"
|
||||
ToolManufacturer = "Aqua Security Software Ltd."
|
||||
Namespace = ToolVendor + ":" + ToolName + ":"
|
||||
ToolVendor = "aquasecurity"
|
||||
ToolName = "trivy"
|
||||
Namespace = ToolVendor + ":" + ToolName + ":"
|
||||
|
||||
// https://json-schema.org/understanding-json-schema/reference/string.html#dates-and-times
|
||||
timeLayout = "2006-01-02T15:04:05+00:00"
|
||||
@@ -89,11 +88,10 @@ func (m *Marshaler) Metadata(ctx context.Context) *cdx.Metadata {
|
||||
Tools: &cdx.ToolsChoice{
|
||||
Components: &[]cdx.Component{
|
||||
{
|
||||
Type: cdx.ComponentTypeApplication,
|
||||
Group: ToolVendor,
|
||||
Name: ToolName,
|
||||
Version: m.appVersion,
|
||||
Manufacturer: &cdx.OrganizationalEntity{Name: ToolManufacturer},
|
||||
Type: cdx.ComponentTypeApplication,
|
||||
Group: ToolVendor,
|
||||
Name: ToolName,
|
||||
Version: m.appVersion,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -287,9 +287,6 @@ func TestMarshaler_MarshalReport(t *testing.T) {
|
||||
Name: "trivy",
|
||||
Group: "aquasecurity",
|
||||
Version: "dev",
|
||||
Manufacturer: &cdx.OrganizationalEntity{
|
||||
Name: "Aqua Security Software Ltd.",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -926,9 +923,6 @@ func TestMarshaler_MarshalReport(t *testing.T) {
|
||||
Name: "trivy",
|
||||
Group: "aquasecurity",
|
||||
Version: "dev",
|
||||
Manufacturer: &cdx.OrganizationalEntity{
|
||||
Name: "Aqua Security Software Ltd.",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1314,9 +1308,6 @@ func TestMarshaler_MarshalReport(t *testing.T) {
|
||||
Name: "trivy",
|
||||
Group: "aquasecurity",
|
||||
Version: "dev",
|
||||
Manufacturer: &cdx.OrganizationalEntity{
|
||||
Name: "Aqua Security Software Ltd.",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1544,9 +1535,6 @@ func TestMarshaler_MarshalReport(t *testing.T) {
|
||||
Name: "trivy",
|
||||
Group: "aquasecurity",
|
||||
Version: "dev",
|
||||
Manufacturer: &cdx.OrganizationalEntity{
|
||||
Name: "Aqua Security Software Ltd.",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1803,9 +1791,6 @@ func TestMarshaler_MarshalReport(t *testing.T) {
|
||||
Name: "trivy",
|
||||
Group: "aquasecurity",
|
||||
Version: "dev",
|
||||
Manufacturer: &cdx.OrganizationalEntity{
|
||||
Name: "Aqua Security Software Ltd.",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1993,9 +1978,6 @@ func TestMarshaler_MarshalReport(t *testing.T) {
|
||||
Name: "trivy",
|
||||
Group: "aquasecurity",
|
||||
Version: "dev",
|
||||
Manufacturer: &cdx.OrganizationalEntity{
|
||||
Name: "Aqua Security Software Ltd.",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2084,9 +2066,6 @@ func TestMarshaler_MarshalReport(t *testing.T) {
|
||||
Name: "trivy",
|
||||
Group: "aquasecurity",
|
||||
Version: "dev",
|
||||
Manufacturer: &cdx.OrganizationalEntity{
|
||||
Name: "Aqua Security Software Ltd.",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user