Compare commits

..

15 Commits

Author SHA1 Message Date
DmitriyLewen
e988fbfebf docs: mage docs:generate 2025-06-27 11:54:43 +06:00
DmitriyLewen
2da74d6aaa chore(deps): bump trivy-db 2025-06-27 11:54:31 +06:00
DmitriyLewen
b8853b242f test: refactor IsVulnerable testcases 2025-06-27 11:49:18 +06:00
DmitriyLewen
50d153b495 chore: add comment for IsSupportedVersion 2025-06-27 11:18:55 +06:00
DmitriyLewen
9b35e6b11f Merge branch 'main' into '(feat/rootio-support' 2025-06-27 11:13:17 +06:00
DmitriyLewen
71ab2b8926 test: add tests for rootio 2025-06-26 19:06:14 +06:00
DmitriyLewen
978276ddd0 refactor: use AND logic for constraint.go 2025-06-26 19:04:32 +06:00
knqyf263
3b54435520 docs: add Root.io documentation
- Add Root.io documentation in others directory
- Update vulnerability scanner data sources to include Root.io
- Add Root.io to mkdocs navigation
- Update others index to include Root.io images
2025-06-26 12:26:45 +04:00
knqyf263
2ed3d7e6c2 refactor: replace mock VulnSrc with trivy-db integration
- Remove mock VulnSrc implementation
- Use real trivy-db rootio.VulnSrc
- Update go.mod to use trivy-db fork with Root.io support
- Fix FixedVersion field mapping to use PatchedVersions
2025-06-26 12:05:55 +04:00
knqyf263
1885e35fa6 fix: resolve linter issues
- Fix empty error handling blocks in vulnsrc.go by properly returning errors
- Add missing newline at end of driver.go file
- Fix import order in provider_test.go as flagged by linter
- All lint checks now pass with 0 issues
2025-06-25 18:52:34 +04:00
knqyf263
4d85a82749 fix: correct Alpine Root.io pattern and improve tests
- Fix Alpine Root.io pattern from 'roo7' to '-r\d007\d' (e.g., -r10071, -r20072)
- Change Debian/Ubuntu pattern from 'root.io' to '.root.io' using regex
- Unify pattern matching using single hasPackageWithPattern function with precompiled regex
- Simplify tests to only test public Provider function using rootio_test package
- Remove redundant private function tests as requested
- Fix import sorting in test file
2025-06-25 18:49:28 +04:00
knqyf263
680358743f refactor: improve Root.io scanner implementation
- Simplify Scanner struct by removing baseOS field (only used in NewScanner)
- Add logger field with Root.io prefix for better debugging
- Improve constraint checking logic to use VulnerableVersions array
- Update isVulnerable method to handle constraint-based vulnerability detection
- Add proper error handling and logging in constraint validation
- Enhance comments and documentation
2025-06-25 18:40:17 +04:00
knqyf263
c902f4ba90 feat: implement realistic VulnSrc for Root.io
- Add realistic mockVulnSrc that integrates with existing OS-specific VulnSrc implementations
- Fetch advisories from original distributors (Debian, Ubuntu, Alpine)
- Transform fixed versions to constraint format (VulnerableVersions, PatchedVersions)
- Merge advisories from base OS and Root.io sources
- Use actual trivy-db components for more realistic behavior
2025-06-25 18:39:46 +04:00
knqyf263
44f068caa2 feat: add root.io support for container image scanning
- Add version constraint library supporting 5 operators (>, <, >=, <=, ==, !=)
- Implement DriverProvider pattern for dynamic driver selection
- Add Root.io environment detection based on package version suffixes
- Support Debian/Ubuntu (root.io suffix) and Alpine (roo7 suffix)
- Add constraint-based vulnerability detection using version ranges
- Include comprehensive tests for all components
2025-06-25 17:35:59 +04:00
knqyf263
317fa2fe85 feat: add version constraint library for Root.io support
- Add constraint.go with support for 5 operators (>, <, >=, <=, ==, !=)
- Add compare.go with DEBComparer and APKComparer implementations
- Support both comma and space separated constraints
- Return errors for empty constraints/versions for security
- Comprehensive test coverage with version_test package
- All linter issues resolved
2025-06-25 15:22:03 +04:00
55 changed files with 407 additions and 1334 deletions

View File

@@ -67,7 +67,6 @@ jobs:
distroless
windows
minimos
rootio
# Languages
ruby

View File

@@ -1 +1 @@
{".":"0.64.1"}
{".":"0.63.0"}

View File

@@ -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)

View File

@@ -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
```

View File

@@ -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>.

View File

@@ -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

View File

@@ -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
View File

@@ -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
View File

@@ -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=

View File

@@ -10,9 +10,6 @@
"components": [
{
"type": "application",
"manufacturer": {
"name": "Aqua Security Software Ltd."
},
"group": "aquasecurity",
"name": "trivy",
"version": "dev"

View File

@@ -10,9 +10,6 @@
"components": [
{
"type": "application",
"manufacturer": {
"name": "Aqua Security Software Ltd."
},
"group": "aquasecurity",
"name": "trivy",
"version": "dev"

View File

@@ -10,9 +10,6 @@
"components": [
{
"type": "application",
"manufacturer": {
"name": "Aqua Security Software Ltd."
},
"group": "aquasecurity",
"name": "trivy",
"version": "dev"

View File

@@ -10,9 +10,6 @@
"components": [
{
"type": "application",
"manufacturer": {
"name": "Aqua Security Software Ltd."
},
"group": "aquasecurity",
"name": "trivy",
"version": "dev"

View File

@@ -10,9 +10,6 @@
"components": [
{
"type": "application",
"manufacturer": {
"name": "Aqua Security Software Ltd."
},
"group": "aquasecurity",
"name": "trivy",
"version": "dev"

View File

@@ -10,9 +10,6 @@
"components": [
{
"type": "application",
"manufacturer": {
"name": "Aqua Security Software Ltd."
},
"group": "aquasecurity",
"name": "trivy",
"version": "dev"

View File

@@ -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(),
}
}

View File

@@ -263,7 +263,6 @@ markdown_extensions:
- pymdownx.highlight
- pymdownx.details
- pymdownx.magiclink
- pymdownx.snippets
- pymdownx.superfences:
custom_fences:
- name: mermaid

View File

@@ -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

View File

@@ -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
}
}

View File

@@ -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,

View File

@@ -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)
}

View File

@@ -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",

View File

@@ -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,
})

View File

@@ -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",
},
},
},
{

View File

@@ -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

View File

@@ -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())
})
}
}

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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
}

View File

@@ -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",
}
)

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",
}
)

View File

@@ -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(),

View File

@@ -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
}
}

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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
}
}

View File

@@ -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 := &rego.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 := &rego.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 := &rego.StaticMetadata{Deprecated: tt.deprecated}
filter := rego.IncludeDeprecatedFilter(tt.include)
result := filter(module, metadata)
assert.Equal(t, tt.expected, result)
})
}
}

View File

@@ -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,

View File

@@ -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}
}

View File

@@ -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
}
}
}

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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
}
}

View File

@@ -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
}

View 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
}
}

View File

@@ -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,
},
},
},

View File

@@ -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.",
},
},
},
},