mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-15 09:00:59 -08:00
feat(vuln): Add --detection-priority flag for accuracy tuning (#7288)
Signed-off-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
@@ -11,9 +11,9 @@ The following scanners are supported.
|
||||
The following table provides an outline of the features Trivy offers.
|
||||
|
||||
|
||||
| Package manager | File | Transitive dependencies | Dev dependencies | [Dependency graph][dependency-graph] | Position |
|
||||
|-------------------------|--------------|:-----------------------:|:----------------:|:------------------------------------:|:--------:|
|
||||
| [Dart][dart-repository] | pubspec.lock | ✓ | Included | ✓ | - |
|
||||
| Package manager | File | Transitive dependencies | Dev dependencies | [Dependency graph][dependency-graph] | Position | [Detection Priority][detection-priority] |
|
||||
|-------------------------|--------------|:-----------------------:|:----------------:|:------------------------------------:|:--------:|:----------------------------------------:|
|
||||
| [Dart][dart-repository] | pubspec.lock | ✓ | Included | ✓ | - | ✓ |
|
||||
|
||||
## Dart
|
||||
In order to detect dependencies, Trivy searches for `pubspec.lock`.
|
||||
@@ -22,11 +22,13 @@ Trivy marks indirect dependencies, but `pubspec.lock` file doesn't have options
|
||||
So Trivy includes all dependencies in report.
|
||||
|
||||
### SDK dependencies
|
||||
Dart uses version `0.0.0` for SDK dependencies (e.g. Flutter). It is not possible to accurately determine the versions of these dependencies.
|
||||
Dart uses version `0.0.0` for SDK dependencies (e.g. Flutter).
|
||||
It is not possible to accurately determine the versions of these dependencies.
|
||||
Trivy just treats them as `0.0.0`.
|
||||
|
||||
Therefore, we use the first version of the constraint for the SDK.
|
||||
If [--detection-priority comprehensive][detection-priority] is passed, Trivy uses the minimum version of the constraint for the SDK.
|
||||
For example, in the following case, the version of `flutter` would be `3.3.0`:
|
||||
|
||||
For example in this case the version of `flutter` should be `3.3.0`:
|
||||
```yaml
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
@@ -40,6 +42,7 @@ sdks:
|
||||
|
||||
### Dependency tree
|
||||
To build `dependency tree` Trivy parses [cache directory][cache-directory]. Currently supported default directories and `PUB_CACHE` environment (absolute path only).
|
||||
|
||||
!!! note
|
||||
Make sure the cache directory contains all the dependencies installed in your application. To download missing dependencies, use `dart pub get` command.
|
||||
|
||||
@@ -47,3 +50,4 @@ To build `dependency tree` Trivy parses [cache directory][cache-directory]. Curr
|
||||
[dart-repository]: https://pub.dev/
|
||||
[dependency-graph]: ../../configuration/reporting.md#show-origins-of-vulnerable-dependencies
|
||||
[cache-directory]: https://dart.dev/tools/pub/glossary#system-cache
|
||||
[detection-priority]: ../../scanner/vulnerability.md#detection-priority
|
||||
@@ -16,10 +16,10 @@ The following scanners are supported.
|
||||
|
||||
The table below provides an outline of the features Trivy offers.
|
||||
|
||||
| Artifact | Offline[^1] | Dev dependencies | [Dependency graph][dependency-graph] | Stdlib |
|
||||
|----------|:-----------:|:-----------------|:------------------------------------:|:------:|
|
||||
| Modules | ✅ | Include | ✅[^2] | - |
|
||||
| Binaries | ✅ | Exclude | - | ✅[^4] |
|
||||
| Artifact | Offline[^1] | Dev dependencies | [Dependency graph][dependency-graph] | Stdlib | [Detection Priority][detection-priority] |
|
||||
|----------|:-----------:|:-----------------|:------------------------------------:|:------:|:----------------------------------------:|
|
||||
| Modules | ✅ | Include | ✅[^2] | - | - |
|
||||
| Binaries | ✅ | Exclude | - | ✅[^4] | Not needed |
|
||||
|
||||
!!! note
|
||||
Trivy scans only dependencies of the Go project.
|
||||
@@ -95,3 +95,4 @@ empty if it cannot do so[^5]. For the second case, the version of such packages
|
||||
[^5]: See https://github.com/golang/go/issues/63432#issuecomment-1751610604
|
||||
|
||||
[dependency-graph]: ../../configuration/reporting.md#show-origins-of-vulnerable-dependencies
|
||||
[detection-priority]: ../../scanner/vulnerability.md#detection-priority
|
||||
|
||||
@@ -12,12 +12,12 @@ Each artifact supports the following scanners:
|
||||
|
||||
The following table provides an outline of the features Trivy offers.
|
||||
|
||||
| Artifact | Internet access | Dev dependencies | [Dependency graph][dependency-graph] | Position |
|
||||
|------------------|:---------------------:|:----------------:|:------------------------------------:|:--------:|
|
||||
| JAR/WAR/PAR/EAR | Trivy Java DB | Include | - | - |
|
||||
| pom.xml | Maven repository [^1] | Exclude | ✓ | ✓[^7] |
|
||||
| *gradle.lockfile | - | Exclude | ✓ | ✓ |
|
||||
| *.sbt.lock | - | Exclude | - | ✓ |
|
||||
| Artifact | Internet access | Dev dependencies | [Dependency graph][dependency-graph] | Position | [Detection Priority][detection-priority] |
|
||||
|------------------|:---------------------:|:----------------:|:------------------------------------:|:--------:|:----------------------------------------:|
|
||||
| JAR/WAR/PAR/EAR | Trivy Java DB | Include | - | - | Not needed |
|
||||
| pom.xml | Maven repository [^1] | Exclude | ✓ | ✓[^7] | - |
|
||||
| *gradle.lockfile | - | Exclude | ✓ | ✓ | Not needed |
|
||||
| *.sbt.lock | - | Exclude | - | ✓ | Not needed |
|
||||
|
||||
These may be enabled or disabled depending on the target.
|
||||
See [here](./index.md) for the detail.
|
||||
@@ -119,3 +119,4 @@ Make sure that you have cache[^8] directory to find licenses from `*.pom` depend
|
||||
[maven-central]: https://repo.maven.apache.org/maven2/
|
||||
[maven-pom-repos]: https://maven.apache.org/settings.html#repositories
|
||||
[sbt-dependency-lock]: https://stringbean.github.io/sbt-dependency-lock
|
||||
[detection-priority]: ../../scanner/vulnerability.md#detection-priority
|
||||
|
||||
@@ -21,11 +21,11 @@ The following scanners are supported for Python packages.
|
||||
|
||||
The following table provides an outline of the features Trivy offers.
|
||||
|
||||
| Package manager | File | Transitive dependencies | Dev dependencies | [Dependency graph][dependency-graph] | Position |
|
||||
|-----------------|------------------|:-----------------------:|:----------------:|:------------------------------------:|:--------:|
|
||||
| pip | requirements.txt | - | Include | - | ✓ |
|
||||
| Pipenv | Pipfile.lock | ✓ | Include | - | ✓ |
|
||||
| Poetry | poetry.lock | ✓ | Exclude | ✓ | - |
|
||||
| Package manager | File | Transitive dependencies | Dev dependencies | [Dependency graph][dependency-graph] | Position | [Detection Priority][detection-priority] |
|
||||
|-----------------|------------------|:-----------------------:|:----------------:|:------------------------------------:|:--------:|:----------------------------------------:|
|
||||
| pip | requirements.txt | - | Include | - | ✓ | - |
|
||||
| Pipenv | Pipfile.lock | ✓ | Include | - | ✓ | Not needed |
|
||||
| Poetry | poetry.lock | ✓ | Exclude | ✓ | - | Not needed |
|
||||
|
||||
|
||||
| Packaging | Dependency graph |
|
||||
@@ -130,3 +130,4 @@ Trivy looks for `.dist-info/META-DATA` to identify Python packages.
|
||||
[^1]: Trivy checks `python`, `python3`, `python2` and `python.exe` file names.
|
||||
|
||||
[dependency-graph]: ../../configuration/reporting.md#show-origins-of-vulnerable-dependencies
|
||||
[detection-priority]: ../../scanner/vulnerability.md#detection-priority
|
||||
|
||||
@@ -8,6 +8,9 @@ Trivy supports the following scanners for Conda packages.
|
||||
| Vulnerability | - |
|
||||
| License | ✓ |
|
||||
|
||||
| Package manager | File | Transitive dependencies | Dev dependencies | [Dependency graph][dependency-graph] | Position | [Detection Priority][detection-priority] |
|
||||
|-----------------|-----------------|:-----------------------:|:----------------:|:------------------------------------:|:--------:|:----------------------------------------:|
|
||||
| Conda | environment.yml | - | Include | - | ✓ | - |
|
||||
|
||||
|
||||
## `<package>.json`
|
||||
@@ -41,3 +44,5 @@ To correctly define licenses, make sure your `environment.yml`[^1] contains `pre
|
||||
[environment.yml]: https://conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html#sharing-an-environment
|
||||
[env-version-range]: https://docs.conda.io/projects/conda-build/en/latest/resources/package-spec.html#examples-of-package-specs
|
||||
[prefix]: https://conda.io/projects/conda/en/latest/user-guide/tasks/manage-environments.html#specifying-a-location-for-an-environment
|
||||
[dependency-graph]: ../../configuration/reporting.md#show-origins-of-vulnerable-dependencies
|
||||
[detection-priority]: ../../scanner/vulnerability.md#detection-priority
|
||||
|
||||
@@ -30,6 +30,10 @@ trivy filesystem [flags] PATH
|
||||
--custom-headers strings custom headers in client mode
|
||||
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2")
|
||||
--dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages
|
||||
--detection-priority string specify the detection priority:
|
||||
- "precise": Prioritizes precise by minimizing false positives.
|
||||
- "comprehensive": Aims to detect more security findings at the cost of potential false positives.
|
||||
(precise,comprehensive) (default "precise")
|
||||
--download-db-only download/update vulnerability database but don't run a scan
|
||||
--download-java-db-only download/update Java index database but don't run a scan
|
||||
--enable-modules strings [EXPERIMENTAL] module names to enable
|
||||
|
||||
@@ -44,6 +44,10 @@ trivy image [flags] IMAGE_NAME
|
||||
--custom-headers strings custom headers in client mode
|
||||
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2")
|
||||
--dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages
|
||||
--detection-priority string specify the detection priority:
|
||||
- "precise": Prioritizes precise by minimizing false positives.
|
||||
- "comprehensive": Aims to detect more security findings at the cost of potential false positives.
|
||||
(precise,comprehensive) (default "precise")
|
||||
--docker-host string unix domain socket path to use for docker scanning
|
||||
--download-db-only download/update vulnerability database but don't run a scan
|
||||
--download-java-db-only download/update Java index database but don't run a scan
|
||||
|
||||
@@ -39,6 +39,10 @@ trivy kubernetes [flags] [CONTEXT]
|
||||
--config-data strings specify paths from which data for the Rego checks will be recursively loaded
|
||||
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2")
|
||||
--dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages
|
||||
--detection-priority string specify the detection priority:
|
||||
- "precise": Prioritizes precise by minimizing false positives.
|
||||
- "comprehensive": Aims to detect more security findings at the cost of potential false positives.
|
||||
(precise,comprehensive) (default "precise")
|
||||
--disable-node-collector When the flag is activated, the node-collector job will not be executed, thus skipping misconfiguration findings on the node.
|
||||
--download-db-only download/update vulnerability database but don't run a scan
|
||||
--download-java-db-only download/update Java index database but don't run a scan
|
||||
|
||||
@@ -30,6 +30,10 @@ trivy repository [flags] (REPO_PATH | REPO_URL)
|
||||
--custom-headers strings custom headers in client mode
|
||||
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2")
|
||||
--dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages
|
||||
--detection-priority string specify the detection priority:
|
||||
- "precise": Prioritizes precise by minimizing false positives.
|
||||
- "comprehensive": Aims to detect more security findings at the cost of potential false positives.
|
||||
(precise,comprehensive) (default "precise")
|
||||
--download-db-only download/update vulnerability database but don't run a scan
|
||||
--download-java-db-only download/update Java index database but don't run a scan
|
||||
--enable-modules strings [EXPERIMENTAL] module names to enable
|
||||
|
||||
@@ -32,6 +32,10 @@ trivy rootfs [flags] ROOTDIR
|
||||
--custom-headers strings custom headers in client mode
|
||||
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2")
|
||||
--dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages
|
||||
--detection-priority string specify the detection priority:
|
||||
- "precise": Prioritizes precise by minimizing false positives.
|
||||
- "comprehensive": Aims to detect more security findings at the cost of potential false positives.
|
||||
(precise,comprehensive) (default "precise")
|
||||
--download-db-only download/update vulnerability database but don't run a scan
|
||||
--download-java-db-only download/update Java index database but don't run a scan
|
||||
--enable-modules strings [EXPERIMENTAL] module names to enable
|
||||
|
||||
@@ -25,6 +25,10 @@ trivy sbom [flags] SBOM_PATH
|
||||
--compliance string compliance report to generate
|
||||
--custom-headers strings custom headers in client mode
|
||||
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2")
|
||||
--detection-priority string specify the detection priority:
|
||||
- "precise": Prioritizes precise by minimizing false positives.
|
||||
- "comprehensive": Aims to detect more security findings at the cost of potential false positives.
|
||||
(precise,comprehensive) (default "precise")
|
||||
--download-db-only download/update vulnerability database but don't run a scan
|
||||
--download-java-db-only download/update Java index database but don't run a scan
|
||||
--exit-code int specify exit code when any security issues are found
|
||||
|
||||
@@ -28,6 +28,10 @@ trivy vm [flags] VM_IMAGE
|
||||
--custom-headers strings custom headers in client mode
|
||||
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2")
|
||||
--dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages
|
||||
--detection-priority string specify the detection priority:
|
||||
- "precise": Prioritizes precise by minimizing false positives.
|
||||
- "comprehensive": Aims to detect more security findings at the cost of potential false positives.
|
||||
(precise,comprehensive) (default "precise")
|
||||
--download-db-only download/update vulnerability database but don't run a scan
|
||||
--download-java-db-only download/update Java index database but don't run a scan
|
||||
--enable-modules strings [EXPERIMENTAL] module names to enable
|
||||
|
||||
@@ -198,6 +198,54 @@ The default is `ghcr.io/aquasecurity/trivy-java-db`.
|
||||
If authentication is required, you need to run `docker login YOUR_REGISTRY`.
|
||||
Currently, specifying a username and password is not supported.
|
||||
|
||||
## Detection Behavior
|
||||
Trivy prioritizes precision in vulnerability detection, aiming to minimize false positives while potentially accepting some false negatives.
|
||||
This approach is particularly relevant in two key areas:
|
||||
|
||||
- Handling Software Installed via OS Packages
|
||||
- Handling Packages with Unspecified Versions
|
||||
|
||||
### Handling Software Installed via OS Packages
|
||||
For files installed by OS package managers, such as `apt`, Trivy exclusively uses advisories from the OS vendor.
|
||||
This means that even if a JAR file is present in a container image, if it was installed via an OS package manager (e.g., `apt`), Trivy will not analyze the JAR file itself and use upstream security advisories.
|
||||
|
||||
For example, consider the Python `requests` package in Red Hat Universal Base Image 8:
|
||||
|
||||
```bash
|
||||
[root@987ee49dc93d /]# head -n 3 /usr/lib/python3.6/site-packages/requests-2.20.0-py3.6.egg-info/PKG-INFO
|
||||
Metadata-Version: 2.1
|
||||
Name: requests
|
||||
Version: 2.20.0
|
||||
```
|
||||
|
||||
Version 2.20.0 is installed, and this package is installed by `dnf`.
|
||||
|
||||
```bash
|
||||
[root@987ee49dc93d /]# rpm -ql python3-requests | grep PKG-INFO
|
||||
/usr/lib/python3.6/site-packages/requests-2.20.0-py3.6.egg-info/PKG-INFO
|
||||
```
|
||||
|
||||
At first glance, this might seem vulnerable to [CVE-2023-32681], which affects versions of requests prior to v2.31.0.
|
||||
However, Red Hat backported the fix to v2.20.0-3 in [RHSA-2023:4520], and the package is not vulnerable.
|
||||
|
||||
- Upstream (PyPI [requests]): Fixed in v2.31.0
|
||||
- Red Hat (`python-requests`): Backported fix applied in v2.20.0-3 (RHSA-2023:4520)
|
||||
|
||||
If Trivy were to detect CVE-2023-32681 in this case, it would be a false positive.
|
||||
This illustrates why using the correct security advisory is crucial to avoid false detections.
|
||||
To minimize false positives, Trivy trusts the OS vendor's advisory for software installed via OS package managers and does not use upstream advisories for these packages.
|
||||
|
||||
However, this approach may lead to false negatives if the OS vendor's advisories are delayed or missing.
|
||||
In such cases, using [--detection-priority comprehensive](#detection-priority) allows Trivy to consider upstream advisories (e.g., [GitHub Advisory Database][ghsa]), potentially increasing false positives but reducing false negatives.
|
||||
|
||||
### Handling Packages with Unspecified Versions
|
||||
When a package version cannot be uniquely determined (e.g., `package-a: ">=3.0"`), Trivy typically skips vulnerability detection for that package to avoid false positives.
|
||||
If a lock file is present with fixed versions, Trivy will use those for detection.
|
||||
|
||||
To detect potential vulnerabilities even with unspecified versions, use [--detection-priority comprehensive](#detection-priority).
|
||||
This option makes Trivy use the minimum version in the specified range for vulnerability detection.
|
||||
While this may increase false positives if the actual version used is not the minimum, it helps reduce false negatives.
|
||||
|
||||
## Configuration
|
||||
This section describes vulnerability-specific configuration.
|
||||
Other common options are documented [here](../configuration/index.md).
|
||||
@@ -307,6 +355,25 @@ By default, all relationships are included in the scan.
|
||||
!!! warning
|
||||
As it may not provide a complete package list, `--pkg-relationships` cannot be used with `--dependency-tree`, `--vex` or SBOM generation.
|
||||
|
||||
### Detection Priority
|
||||
|
||||
Trivy provides a `--detection-priority` flag to control the balance between false positives and false negatives in vulnerability detection.
|
||||
This concept is similar to the relationship between [precision and recall][precision-recall] in machine learning evaluation.
|
||||
|
||||
```bash
|
||||
$ trivy image --detection-priority {precise|comprehensive} alpine:3.15
|
||||
```
|
||||
|
||||
- `precise`: This mode prioritizes reducing false positives. It results in less noisy vulnerability reports but may miss some potential vulnerabilities.
|
||||
- `comprehensive`: This mode aims to detect more vulnerabilities, potentially including some that might be false positives.
|
||||
It provides broader coverage but may increase the noise in the results.
|
||||
|
||||
The default value is `precise`. Also refer to the [detection behavior](#detection-behavior) section for more information.
|
||||
|
||||
Regardless of the chosen mode, user review of detected vulnerabilities is crucial:
|
||||
|
||||
- `precise`: Review thoroughly, considering potential missed vulnerabilities.
|
||||
- `comprehensive`: Carefully investigate each reported vulnerability due to increased false positive possibility.
|
||||
|
||||
[^1]: https://github.com/GoogleContainerTools/distroless
|
||||
|
||||
@@ -353,3 +420,9 @@ By default, all relationships are included in the scan.
|
||||
[nvd]: https://nvd.nist.gov/vuln
|
||||
|
||||
[k8s-cve]: https://kubernetes.io/docs/reference/issues-security/official-cve-feed/
|
||||
|
||||
[CVE-2023-32681]: https://nvd.nist.gov/vuln/detail/CVE-2023-32681
|
||||
[RHSA-2023:4520]: https://access.redhat.com/errata/RHSA-2023:4520
|
||||
[ghsa]: https://github.com/advisories
|
||||
[requests]: https://pypi.org/project/requests/
|
||||
[precision-recall]: https://developers.google.com/machine-learning/crash-course/classification/precision-and-recall
|
||||
@@ -287,7 +287,7 @@ func TestClientServer(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
osArgs := setupClient(t, tt.args, addr, cacheDir, tt.golden)
|
||||
osArgs := setupClient(t, tt.args, addr, cacheDir)
|
||||
|
||||
if tt.args.secretConfig != "" {
|
||||
osArgs = append(osArgs, "--secret-config", tt.args.secretConfig)
|
||||
@@ -407,7 +407,7 @@ func TestClientServerWithFormat(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Setenv("AWS_REGION", "test-region")
|
||||
t.Setenv("AWS_ACCOUNT_ID", "123456789012")
|
||||
osArgs := setupClient(t, tt.args, addr, cacheDir, tt.golden)
|
||||
osArgs := setupClient(t, tt.args, addr, cacheDir)
|
||||
|
||||
runTest(t, osArgs, tt.golden, "", tt.args.Format, runOptions{
|
||||
override: overrideUID,
|
||||
@@ -435,7 +435,7 @@ func TestClientServerWithCycloneDX(t *testing.T) {
|
||||
addr, cacheDir := setup(t, setupOptions{})
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
osArgs := setupClient(t, tt.args, addr, cacheDir, tt.golden)
|
||||
osArgs := setupClient(t, tt.args, addr, cacheDir)
|
||||
runTest(t, osArgs, tt.golden, "", types.FormatCycloneDX, runOptions{
|
||||
fakeUUID: "3ff14136-e09f-4df9-80ea-%012d",
|
||||
})
|
||||
@@ -488,7 +488,7 @@ func TestClientServerWithToken(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
osArgs := setupClient(t, tt.args, addr, cacheDir, tt.golden)
|
||||
osArgs := setupClient(t, tt.args, addr, cacheDir)
|
||||
runTest(t, osArgs, tt.golden, "", types.FormatJSON, runOptions{
|
||||
override: overrideUID,
|
||||
wantErr: tt.wantErr,
|
||||
@@ -515,7 +515,7 @@ func TestClientServerWithRedis(t *testing.T) {
|
||||
golden := "testdata/alpine-39.json.golden"
|
||||
|
||||
t.Run("alpine 3.9", func(t *testing.T) {
|
||||
osArgs := setupClient(t, testArgs, addr, cacheDir, golden)
|
||||
osArgs := setupClient(t, testArgs, addr, cacheDir)
|
||||
|
||||
// Run Trivy client
|
||||
runTest(t, osArgs, golden, "", types.FormatJSON, runOptions{
|
||||
@@ -527,7 +527,7 @@ func TestClientServerWithRedis(t *testing.T) {
|
||||
require.NoError(t, redisC.Terminate(ctx))
|
||||
|
||||
t.Run("sad path", func(t *testing.T) {
|
||||
osArgs := setupClient(t, testArgs, addr, cacheDir, golden)
|
||||
osArgs := setupClient(t, testArgs, addr, cacheDir)
|
||||
|
||||
// Run Trivy client
|
||||
runTest(t, osArgs, "", "", types.FormatJSON, runOptions{
|
||||
@@ -592,7 +592,7 @@ func setupServer(addr, token, tokenHeader, cacheDir, cacheBackend string) []stri
|
||||
return osArgs
|
||||
}
|
||||
|
||||
func setupClient(t *testing.T, c csArgs, addr string, cacheDir string, golden string) []string {
|
||||
func setupClient(t *testing.T, c csArgs, addr string, cacheDir string) []string {
|
||||
if c.Command == "" {
|
||||
c.Command = "image"
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -22,6 +23,7 @@ func TestTar(t *testing.T) {
|
||||
Input string
|
||||
SkipDirs []string
|
||||
SkipFiles []string
|
||||
DetectionPriority ftypes.DetectionPriority
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -274,6 +276,15 @@ func TestTar(t *testing.T) {
|
||||
},
|
||||
golden: "testdata/ubi-7.json.golden",
|
||||
},
|
||||
{
|
||||
name: "ubi 7 with comprehensive priority",
|
||||
args: args{
|
||||
Format: types.FormatJSON,
|
||||
Input: "testdata/fixtures/images/ubi-7.tar.gz",
|
||||
DetectionPriority: ftypes.PriorityComprehensive,
|
||||
},
|
||||
golden: "testdata/ubi-7-comprehensive.json.golden",
|
||||
},
|
||||
{
|
||||
name: "almalinux 8",
|
||||
args: args{
|
||||
@@ -380,7 +391,7 @@ func TestTar(t *testing.T) {
|
||||
"-q",
|
||||
"--format",
|
||||
string(tt.args.Format),
|
||||
"--skip-update",
|
||||
"--skip-db-update",
|
||||
}
|
||||
|
||||
if tt.args.IgnoreUnfixed {
|
||||
@@ -411,6 +422,10 @@ func TestTar(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
if tt.args.DetectionPriority != "" {
|
||||
osArgs = append(osArgs, "--detection-priority", string(tt.args.DetectionPriority))
|
||||
}
|
||||
|
||||
// Run Trivy
|
||||
runTest(t, osArgs, tt.golden, "", tt.args.Format, runOptions{})
|
||||
})
|
||||
|
||||
8
integration/testdata/fixtures/db/python.yaml
vendored
8
integration/testdata/fixtures/db/python.yaml
vendored
@@ -14,3 +14,11 @@
|
||||
- 0.11.6
|
||||
VulnerableVersions:
|
||||
- < 0.11.6
|
||||
- bucket: setuptools
|
||||
pairs:
|
||||
- key: CVE-2022-40897
|
||||
value:
|
||||
PatchedVersions:
|
||||
- 65.5.1
|
||||
VulnerableVersions:
|
||||
- < 65.5.1
|
||||
@@ -1400,3 +1400,22 @@
|
||||
- "https://nvd.nist.gov/vuln/detail/CVE-2020-14155"
|
||||
PublishedDate: "2020-06-15T17:15:00Z"
|
||||
LastModifiedDate: "2022-04-28T15:06:00Z"
|
||||
- key: CVE-2022-40897
|
||||
value:
|
||||
Title: "pypa-setuptools: Regular Expression Denial of Service (ReDoS) in package_index.py"
|
||||
Description: "Python Packaging Authority (PyPA) setuptools before 65.5.1 allows remote attackers to cause a denial of service via HTML in a crafted package or custom PackageIndex page. There is a Regular Expression Denial of Service (ReDoS) in package_index.py."
|
||||
Severity: MEDIUM
|
||||
CweIDs:
|
||||
- CWE-1333
|
||||
VendorSeverity:
|
||||
ghsa: 3
|
||||
nvd: 2
|
||||
CVSS:
|
||||
nvd:
|
||||
V3Vector: "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H"
|
||||
V3Score: 5.9
|
||||
References:
|
||||
- "https://access.redhat.com/errata/RHSA-2023:0952"
|
||||
- "https://access.redhat.com/security/cve/CVE-2022-40897"
|
||||
PublishedDate: "2022-12-23T00:15:13.987Z"
|
||||
LastModifiedDate: "2024-06-21T19:15:23.877Z"
|
||||
|
||||
192
integration/testdata/ubi-7-comprehensive.json.golden
vendored
Normal file
192
integration/testdata/ubi-7-comprehensive.json.golden
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
{
|
||||
"SchemaVersion": 2,
|
||||
"CreatedAt": "2021-08-25T12:20:30.000000005Z",
|
||||
"ArtifactName": "testdata/fixtures/images/ubi-7.tar.gz",
|
||||
"ArtifactType": "container_image",
|
||||
"Metadata": {
|
||||
"OS": {
|
||||
"Family": "redhat",
|
||||
"Name": "7.7"
|
||||
},
|
||||
"ImageID": "sha256:6fecccc91c83e11ae4fede6793e9410841221d4779520c2b9e9fb7f7b3830264",
|
||||
"DiffIDs": [
|
||||
"sha256:4468e6d912c76d5b127f3554c3cd83b7dc07cce6107c6b916299ba76fa7d15ac",
|
||||
"sha256:ecb0311889b3478bc9b62660fa9391d5ebf8da4c6ae143cb33434873668f9e36"
|
||||
],
|
||||
"ImageConfig": {
|
||||
"architecture": "amd64",
|
||||
"created": "2019-09-02T12:56:43.939095Z",
|
||||
"docker_version": "1.13.1",
|
||||
"history": [
|
||||
{
|
||||
"created": "2019-09-02T12:56:36.440695936Z",
|
||||
"comment": "Imported from -"
|
||||
},
|
||||
{
|
||||
"created": "2019-09-02T12:56:43.939095Z"
|
||||
}
|
||||
],
|
||||
"os": "linux",
|
||||
"rootfs": {
|
||||
"type": "layers",
|
||||
"diff_ids": [
|
||||
"sha256:4468e6d912c76d5b127f3554c3cd83b7dc07cce6107c6b916299ba76fa7d15ac",
|
||||
"sha256:ecb0311889b3478bc9b62660fa9391d5ebf8da4c6ae143cb33434873668f9e36"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"Cmd": [
|
||||
"/bin/bash"
|
||||
],
|
||||
"Env": [
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"container=oci"
|
||||
],
|
||||
"Hostname": "0da2e3774382",
|
||||
"Image": "2e9103a7b91a7ffe333e9162ce98ea078263747527571655e93bd4d35ee278f0",
|
||||
"Labels": {
|
||||
"architecture": "x86_64",
|
||||
"authoritative-source-url": "registry.access.redhat.com",
|
||||
"build-date": "2019-09-02T12:56:18.824770",
|
||||
"com.redhat.build-host": "cpt-1005.osbs.prod.upshift.rdu2.redhat.com",
|
||||
"com.redhat.component": "ubi7-container",
|
||||
"com.redhat.license_terms": "https://www.redhat.com/en/about/red-hat-end-user-license-agreements#UBI",
|
||||
"description": "The Universal Base Image is designed and engineered to be the base layer for all of your containerized applications, middleware and utilities. This base image is freely redistributable, but Red Hat only supports Red Hat technologies through subscriptions for Red Hat products. This image is maintained by Red Hat and updated regularly.",
|
||||
"distribution-scope": "public",
|
||||
"io.k8s.description": "The Universal Base Image is designed and engineered to be the base layer for all of your containerized applications, middleware and utilities. This base image is freely redistributable, but Red Hat only supports Red Hat technologies through subscriptions for Red Hat products. This image is maintained by Red Hat and updated regularly.",
|
||||
"io.k8s.display-name": "Red Hat Universal Base Image 7",
|
||||
"io.openshift.tags": "base rhel7",
|
||||
"maintainer": "Red Hat, Inc.",
|
||||
"name": "ubi7",
|
||||
"release": "140",
|
||||
"summary": "Provides the latest release of the Red Hat Universal Base Image 7.",
|
||||
"url": "https://access.redhat.com/containers/#/registry.access.redhat.com/ubi7/images/7.7-140",
|
||||
"vcs-ref": "4c80c8aa26e69950ab11b87789c8fb7665b1632d",
|
||||
"vcs-type": "git",
|
||||
"vendor": "Red Hat, Inc.",
|
||||
"version": "7.7"
|
||||
},
|
||||
"ArgsEscaped": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"Results": [
|
||||
{
|
||||
"Target": "testdata/fixtures/images/ubi-7.tar.gz (redhat 7.7)",
|
||||
"Class": "os-pkgs",
|
||||
"Type": "redhat",
|
||||
"Vulnerabilities": [
|
||||
{
|
||||
"VulnerabilityID": "CVE-2019-18276",
|
||||
"PkgID": "bash@4.2.46-33.el7.x86_64",
|
||||
"PkgName": "bash",
|
||||
"PkgIdentifier": {
|
||||
"PURL": "pkg:rpm/redhat/bash@4.2.46-33.el7?arch=x86_64\u0026distro=redhat-7.7",
|
||||
"UID": "f5b786381193ad1b"
|
||||
},
|
||||
"InstalledVersion": "4.2.46-33.el7",
|
||||
"Status": "will_not_fix",
|
||||
"Layer": {
|
||||
"Digest": "sha256:7b1c937e0f6794db2535be6e4cb6d60a0b668ef78c2576611a3fb9c97a95ccdf",
|
||||
"DiffID": "sha256:4468e6d912c76d5b127f3554c3cd83b7dc07cce6107c6b916299ba76fa7d15ac"
|
||||
},
|
||||
"SeveritySource": "redhat",
|
||||
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2019-18276",
|
||||
"Title": "bash: when effective UID is not equal to its real UID the saved UID is not dropped",
|
||||
"Description": "An issue was discovered in disable_priv_mode in shell.c in GNU Bash through 5.0 patch 11. By default, if Bash is run with its effective UID not equal to its real UID, it will drop privileges by setting its effective UID to its real UID. However, it does so incorrectly. On Linux and other systems that support \"saved UID\" functionality, the saved UID is not dropped. An attacker with command execution in the shell can use \"enable -f\" for runtime loading of a new builtin, which can be a shared object that calls setuid() and therefore regains privileges. However, binaries running with an effective UID of 0 are unaffected.",
|
||||
"Severity": "LOW",
|
||||
"CweIDs": [
|
||||
"CWE-273"
|
||||
],
|
||||
"VendorSeverity": {
|
||||
"cbl-mariner": 3,
|
||||
"nvd": 3,
|
||||
"oracle-oval": 1,
|
||||
"photon": 3,
|
||||
"redhat": 1,
|
||||
"ubuntu": 1
|
||||
},
|
||||
"CVSS": {
|
||||
"nvd": {
|
||||
"V2Vector": "AV:L/AC:L/Au:N/C:C/I:C/A:C",
|
||||
"V3Vector": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
|
||||
"V2Score": 7.2,
|
||||
"V3Score": 7.8
|
||||
},
|
||||
"redhat": {
|
||||
"V3Vector": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
|
||||
"V3Score": 7.8
|
||||
}
|
||||
},
|
||||
"References": [
|
||||
"http://packetstormsecurity.com/files/155498/Bash-5.0-Patch-11-Privilege-Escalation.html",
|
||||
"https://access.redhat.com/security/cve/CVE-2019-18276",
|
||||
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-18276",
|
||||
"https://github.com/bminor/bash/commit/951bdaad7a18cc0dc1036bba86b18b90874d39ff",
|
||||
"https://linux.oracle.com/cve/CVE-2019-18276.html",
|
||||
"https://linux.oracle.com/errata/ELSA-2021-1679.html",
|
||||
"https://lists.apache.org/thread.html/rf9fa47ab66495c78bb4120b0754dd9531ca2ff0430f6685ac9b07772@%3Cdev.mina.apache.org%3E",
|
||||
"https://nvd.nist.gov/vuln/detail/CVE-2019-18276",
|
||||
"https://security.gentoo.org/glsa/202105-34",
|
||||
"https://security.netapp.com/advisory/ntap-20200430-0003/",
|
||||
"https://www.youtube.com/watch?v=-wGtxJ8opa8"
|
||||
],
|
||||
"PublishedDate": "2019-11-28T01:15:00Z",
|
||||
"LastModifiedDate": "2021-05-26T12:15:00Z"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Target": "Python",
|
||||
"Class": "lang-pkgs",
|
||||
"Type": "python-pkg",
|
||||
"Vulnerabilities": [
|
||||
{
|
||||
"VulnerabilityID": "CVE-2022-40897",
|
||||
"PkgName": "setuptools",
|
||||
"PkgPath": "usr/lib/python2.7/site-packages/setuptools-0.9.8-py2.7.egg-info/PKG-INFO",
|
||||
"PkgIdentifier": {
|
||||
"PURL": "pkg:pypi/setuptools@0.9.8",
|
||||
"UID": "3f4c89bf681c1d7a"
|
||||
},
|
||||
"InstalledVersion": "0.9.8",
|
||||
"FixedVersion": "65.5.1",
|
||||
"Status": "fixed",
|
||||
"Layer": {
|
||||
"Digest": "sha256:7b1c937e0f6794db2535be6e4cb6d60a0b668ef78c2576611a3fb9c97a95ccdf",
|
||||
"DiffID": "sha256:4468e6d912c76d5b127f3554c3cd83b7dc07cce6107c6b916299ba76fa7d15ac"
|
||||
},
|
||||
"SeveritySource": "ghsa",
|
||||
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2022-40897",
|
||||
"DataSource": {
|
||||
"ID": "ghsa",
|
||||
"Name": "GitHub Security Advisory Pip",
|
||||
"URL": "https://github.com/advisories?query=type%3Areviewed+ecosystem%3Apip"
|
||||
},
|
||||
"Title": "pypa-setuptools: Regular Expression Denial of Service (ReDoS) in package_index.py",
|
||||
"Description": "Python Packaging Authority (PyPA) setuptools before 65.5.1 allows remote attackers to cause a denial of service via HTML in a crafted package or custom PackageIndex page. There is a Regular Expression Denial of Service (ReDoS) in package_index.py.",
|
||||
"Severity": "HIGH",
|
||||
"CweIDs": [
|
||||
"CWE-1333"
|
||||
],
|
||||
"VendorSeverity": {
|
||||
"ghsa": 3,
|
||||
"nvd": 2
|
||||
},
|
||||
"CVSS": {
|
||||
"nvd": {
|
||||
"V3Vector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
|
||||
"V3Score": 5.9
|
||||
}
|
||||
},
|
||||
"References": [
|
||||
"https://access.redhat.com/errata/RHSA-2023:0952",
|
||||
"https://access.redhat.com/security/cve/CVE-2022-40897"
|
||||
],
|
||||
"PublishedDate": "2022-12-23T00:15:13.987Z",
|
||||
"LastModifiedDate": "2024-06-21T19:15:23.877Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
12
pkg/cache/key.go
vendored
12
pkg/cache/key.go
vendored
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
func CalcKey(id string, analyzerVersions analyzer.Versions, hookVersions map[string]int, artifactOpt artifact.Option) (string, error) {
|
||||
@@ -30,7 +31,16 @@ func CalcKey(id string, analyzerVersions analyzer.Versions, hookVersions map[str
|
||||
SkipFiles []string
|
||||
SkipDirs []string
|
||||
FilePatterns []string `json:",omitempty"`
|
||||
}{id, analyzerVersions, hookVersions, artifactOpt.WalkerOption.SkipFiles, artifactOpt.WalkerOption.SkipDirs, artifactOpt.FilePatterns}
|
||||
DetectionPriority types.DetectionPriority `json:",omitempty"`
|
||||
}{
|
||||
id,
|
||||
analyzerVersions,
|
||||
hookVersions,
|
||||
artifactOpt.WalkerOption.SkipFiles,
|
||||
artifactOpt.WalkerOption.SkipDirs,
|
||||
artifactOpt.FilePatterns,
|
||||
artifactOpt.DetectionPriority,
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(h).Encode(keyBase); err != nil {
|
||||
return "", xerrors.Errorf("json encode error: %w", err)
|
||||
|
||||
31
pkg/cache/key_test.go
vendored
31
pkg/cache/key_test.go
vendored
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/walker"
|
||||
"github.com/aquasecurity/trivy/pkg/misconf"
|
||||
)
|
||||
@@ -23,6 +24,7 @@ func TestCalcKey(t *testing.T) {
|
||||
policy []string
|
||||
data []string
|
||||
secretConfigPath string
|
||||
detectionPriority types.DetectionPriority
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -115,7 +117,10 @@ func TestCalcKey(t *testing.T) {
|
||||
"debian": 1,
|
||||
},
|
||||
},
|
||||
patterns: []string{"test", ""},
|
||||
patterns: []string{
|
||||
"test",
|
||||
"",
|
||||
},
|
||||
},
|
||||
want: "sha256:71abf09bf1422531e2838db692b80f9b9f48766f56b7d3d02aecdb36b019e103",
|
||||
},
|
||||
@@ -129,7 +134,10 @@ func TestCalcKey(t *testing.T) {
|
||||
"debian": 1,
|
||||
},
|
||||
},
|
||||
patterns: []string{"", "test"},
|
||||
patterns: []string{
|
||||
"",
|
||||
"test",
|
||||
},
|
||||
},
|
||||
want: "sha256:71abf09bf1422531e2838db692b80f9b9f48766f56b7d3d02aecdb36b019e103",
|
||||
},
|
||||
@@ -177,6 +185,23 @@ func TestCalcKey(t *testing.T) {
|
||||
},
|
||||
want: "sha256:363f70f4ee795f250873caea11c2fc94ef12945444327e7e2f8a99e3884695e0",
|
||||
},
|
||||
{
|
||||
name: "detection priority",
|
||||
args: args{
|
||||
key: "sha256:5c534be56eca62e756ef2ef51523feda0f19cd7c15bb0c015e3d6e3ae090bf6e",
|
||||
analyzerVersions: analyzer.Versions{
|
||||
Analyzers: map[string]int{
|
||||
"alpine": 1,
|
||||
"debian": 1,
|
||||
},
|
||||
},
|
||||
skipFiles: []string{"app/deployment.yaml"},
|
||||
skipDirs: []string{"usr/java"},
|
||||
policy: []string{"testdata/policy"},
|
||||
detectionPriority: types.PriorityComprehensive,
|
||||
},
|
||||
want: "sha256:2f1c898271e84f4382cd48ae7533069cc3dc656c2d688ac108f5db1a0d9fd393",
|
||||
},
|
||||
{
|
||||
|
||||
name: "secret config",
|
||||
@@ -232,6 +257,7 @@ func TestCalcKey(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
artifactOpt := artifact.Option{
|
||||
FilePatterns: tt.args.patterns,
|
||||
DetectionPriority: tt.args.detectionPriority,
|
||||
|
||||
MisconfScannerOption: misconf.ScannerOption{
|
||||
PolicyPaths: tt.args.policy,
|
||||
@@ -249,7 +275,6 @@ func TestCalcKey(t *testing.T) {
|
||||
}
|
||||
got, err := CalcKey(tt.args.key, tt.args.analyzerVersions, tt.args.hookVersions, artifactOpt)
|
||||
if tt.wantErr != "" {
|
||||
require.Error(t, err)
|
||||
assert.ErrorContains(t, err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -568,6 +568,10 @@ func (r *runner) initScannerConfig(opts flag.Options) (ScannerConfig, types.Scan
|
||||
fileChecksum = true
|
||||
}
|
||||
|
||||
// Disable the post handler for filtering system file when detection priority is comprehensive.
|
||||
disabledHandlers := lo.Ternary(opts.DetectionPriority == ftypes.PriorityComprehensive,
|
||||
[]ftypes.HandlerType{ftypes.SystemFileFilteringPostHandler}, nil)
|
||||
|
||||
return ScannerConfig{
|
||||
Target: target,
|
||||
CacheOptions: opts.CacheOpts(),
|
||||
@@ -579,6 +583,7 @@ func (r *runner) initScannerConfig(opts flag.Options) (ScannerConfig, types.Scan
|
||||
},
|
||||
ArtifactOption: artifact.Option{
|
||||
DisabledAnalyzers: disabledAnalyzers(opts),
|
||||
DisabledHandlers: disabledHandlers,
|
||||
FilePatterns: opts.FilePatterns,
|
||||
Parallel: opts.Parallel,
|
||||
Offline: opts.OfflineScan,
|
||||
@@ -592,6 +597,7 @@ func (r *runner) initScannerConfig(opts flag.Options) (ScannerConfig, types.Scan
|
||||
AWSRegion: opts.Region,
|
||||
AWSEndpoint: opts.Endpoint,
|
||||
FileChecksum: fileChecksum,
|
||||
DetectionPriority: opts.DetectionPriority,
|
||||
|
||||
// For image scanning
|
||||
ImageOption: ftypes.ImageOptions{
|
||||
|
||||
@@ -20,11 +20,13 @@ const (
|
||||
// Parser is a parser for pubspec.lock
|
||||
type Parser struct {
|
||||
logger *log.Logger
|
||||
useMinVersion bool
|
||||
}
|
||||
|
||||
func NewParser() *Parser {
|
||||
func NewParser(useMinVersion bool) *Parser {
|
||||
return &Parser{
|
||||
logger: log.WithPrefix("pub"),
|
||||
useMinVersion: useMinVersion,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +52,7 @@ func (p Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency
|
||||
var pkgs []ftypes.Package
|
||||
for name, dep := range l.Packages {
|
||||
version := dep.Version
|
||||
if version == "0.0.0" && dep.Source == "sdk" {
|
||||
if version == "0.0.0" && dep.Source == "sdk" && p.useMinVersion {
|
||||
version = p.findSDKVersion(l, name, dep)
|
||||
}
|
||||
|
||||
@@ -71,27 +73,27 @@ func (p Parser) Parse(r xio.ReadSeekerAt) ([]ftypes.Package, []ftypes.Dependency
|
||||
return pkgs, nil, nil
|
||||
}
|
||||
|
||||
// findSDKVersion detects the first version of the SDK constraint specified in the Description.
|
||||
// findSDKVersion detects the minimum version of the SDK constraint specified in the Description.
|
||||
// If the constraint is not found, it returns the original version.
|
||||
func (p Parser) findSDKVersion(l *lock, name string, dep Dep) string {
|
||||
// Some dependencies use one of the SDK versions.
|
||||
// In this case dep.Version == `0.0.0`.
|
||||
// We can't get versions for these dependencies.
|
||||
// Therefore, we use the first version of the SDK constraint specified in the Description.
|
||||
// Therefore, we use the minimum version of the SDK constraint specified in the Description.
|
||||
// See https://github.com/aquasecurity/trivy/issues/6017
|
||||
constraint, ok := l.Sdks[string(dep.Description)]
|
||||
if !ok {
|
||||
return dep.Version
|
||||
}
|
||||
|
||||
v, err := firstVersionOfConstrain(constraint)
|
||||
v, err := minVersionOfConstrain(constraint)
|
||||
if err != nil {
|
||||
p.logger.Warn("Unable to get sdk version from constraint", log.Err(err))
|
||||
return dep.Version
|
||||
} else if v == "" {
|
||||
return dep.Version
|
||||
}
|
||||
p.logger.Info("Using the first version of the constraint from the sdk source", log.String("dep", name),
|
||||
p.logger.Info("Using the minimum version of the constraint from the sdk source", log.String("dep", name),
|
||||
log.String("constraint", constraint))
|
||||
return v
|
||||
}
|
||||
@@ -106,8 +108,8 @@ func (p Parser) relationship(dep string) ftypes.Relationship {
|
||||
return ftypes.RelationshipUnknown
|
||||
}
|
||||
|
||||
// firstVersionOfConstrain returns the first acceptable version for constraint
|
||||
func firstVersionOfConstrain(constraint string) (string, error) {
|
||||
// minVersionOfConstrain returns the minimum acceptable version for constraint
|
||||
func minVersionOfConstrain(constraint string) (string, error) {
|
||||
css, err := goversion.NewConstraints(constraint)
|
||||
if err != nil {
|
||||
return "", xerrors.Errorf("unable to parse constraints: %w", err)
|
||||
@@ -119,7 +121,7 @@ func firstVersionOfConstrain(constraint string) (string, error) {
|
||||
if len(constraints) == 0 || len(constraints[0]) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
// We only need to get the first version from the range
|
||||
// We only need to get the minimum version from the range
|
||||
if constraints[0][0].Operator() != ">=" && constraints[0][0].Operator() != "^" {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
@@ -16,12 +16,40 @@ import (
|
||||
func TestParser_Parse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
useMinVersion bool
|
||||
inputFile string
|
||||
want []ftypes.Package
|
||||
wantErr assert.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
name: "not use minimum version",
|
||||
useMinVersion: false,
|
||||
inputFile: "testdata/happy.lock",
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
ID: "crypto@3.0.2",
|
||||
Name: "crypto",
|
||||
Version: "3.0.2",
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "flutter_test@0.0.0",
|
||||
Name: "flutter_test",
|
||||
Version: "0.0.0",
|
||||
Relationship: ftypes.RelationshipDirect,
|
||||
},
|
||||
{
|
||||
ID: "uuid@3.0.6",
|
||||
Name: "uuid",
|
||||
Version: "3.0.6",
|
||||
Relationship: ftypes.RelationshipIndirect,
|
||||
},
|
||||
},
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "use minimum version",
|
||||
useMinVersion: true,
|
||||
inputFile: "testdata/happy.lock",
|
||||
want: []ftypes.Package{
|
||||
{
|
||||
@@ -63,7 +91,7 @@ func TestParser_Parse(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
defer f.Close()
|
||||
|
||||
gotPkgs, _, err := pub.NewParser().Parse(f)
|
||||
gotPkgs, _, err := pub.NewParser(tt.useMinVersion).Parse(f)
|
||||
if !tt.wantErr(t, err, fmt.Sprintf("Parse(%v)", tt.inputFile)) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ type AnalyzerOptions struct {
|
||||
Parallel int
|
||||
FilePatterns []string
|
||||
DisabledAnalyzers []Type
|
||||
DetectionPriority types.DetectionPriority
|
||||
MisconfScannerOption misconf.ScannerOption
|
||||
SecretScannerOption SecretScannerOption
|
||||
LicenseScannerOption LicenseScannerOption
|
||||
@@ -124,6 +125,7 @@ type AnalyzerGroup struct {
|
||||
analyzers []analyzer
|
||||
postAnalyzers []PostAnalyzer
|
||||
filePatterns map[Type][]*regexp.Regexp
|
||||
detectionPriority types.DetectionPriority
|
||||
}
|
||||
|
||||
///////////////////////////
|
||||
@@ -312,8 +314,8 @@ func belongToGroup(groupName Group, analyzerType Type, disabledAnalyzers []Type,
|
||||
|
||||
const separator = ":"
|
||||
|
||||
func NewAnalyzerGroup(opt AnalyzerOptions) (AnalyzerGroup, error) {
|
||||
groupName := opt.Group
|
||||
func NewAnalyzerGroup(opts AnalyzerOptions) (AnalyzerGroup, error) {
|
||||
groupName := opts.Group
|
||||
if groupName == "" {
|
||||
groupName = GroupBuiltin
|
||||
}
|
||||
@@ -321,8 +323,9 @@ func NewAnalyzerGroup(opt AnalyzerOptions) (AnalyzerGroup, error) {
|
||||
group := AnalyzerGroup{
|
||||
logger: log.WithPrefix("analyzer"),
|
||||
filePatterns: make(map[Type][]*regexp.Regexp),
|
||||
detectionPriority: opts.DetectionPriority,
|
||||
}
|
||||
for _, p := range opt.FilePatterns {
|
||||
for _, p := range opts.FilePatterns {
|
||||
// e.g. "dockerfile:my_dockerfile_*"
|
||||
s := strings.SplitN(p, separator, 2)
|
||||
if len(s) != 2 {
|
||||
@@ -343,12 +346,12 @@ func NewAnalyzerGroup(opt AnalyzerOptions) (AnalyzerGroup, error) {
|
||||
}
|
||||
|
||||
for analyzerType, a := range analyzers {
|
||||
if !belongToGroup(groupName, analyzerType, opt.DisabledAnalyzers, a) {
|
||||
if !belongToGroup(groupName, analyzerType, opts.DisabledAnalyzers, a) {
|
||||
continue
|
||||
}
|
||||
// Initialize only scanners that have Init()
|
||||
if ini, ok := a.(Initializer); ok {
|
||||
if err := ini.Init(opt); err != nil {
|
||||
if err := ini.Init(opts); err != nil {
|
||||
return AnalyzerGroup{}, xerrors.Errorf("analyzer initialization error: %w", err)
|
||||
}
|
||||
}
|
||||
@@ -356,11 +359,11 @@ func NewAnalyzerGroup(opt AnalyzerOptions) (AnalyzerGroup, error) {
|
||||
}
|
||||
|
||||
for analyzerType, init := range postAnalyzers {
|
||||
a, err := init(opt)
|
||||
a, err := init(opts)
|
||||
if err != nil {
|
||||
return AnalyzerGroup{}, xerrors.Errorf("post-analyzer init error: %w", err)
|
||||
}
|
||||
if !belongToGroup(groupName, analyzerType, opt.DisabledAnalyzers, a) {
|
||||
if !belongToGroup(groupName, analyzerType, opts.DisabledAnalyzers, a) {
|
||||
continue
|
||||
}
|
||||
group.postAnalyzers = append(group.postAnalyzers, a)
|
||||
@@ -473,6 +476,11 @@ func (ag AnalyzerGroup) PostAnalyze(ctx context.Context, compositeFS *CompositeF
|
||||
}
|
||||
|
||||
skippedFiles := result.SystemInstalledFiles
|
||||
if ag.detectionPriority == types.PriorityComprehensive {
|
||||
// If the detection priority is comprehensive, system files installed by the OS package manager will not be skipped.
|
||||
// It can lead to false positives and duplicates, but it may be necessary to detect all possible vulnerabilities.
|
||||
skippedFiles = nil
|
||||
}
|
||||
for _, app := range result.Applications {
|
||||
skippedFiles = append(skippedFiles, app.FilePath)
|
||||
for _, pkg := range app.Packages {
|
||||
|
||||
@@ -37,10 +37,10 @@ type pubSpecLockAnalyzer struct {
|
||||
parser language.Parser
|
||||
}
|
||||
|
||||
func newPubSpecLockAnalyzer(_ analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) {
|
||||
func newPubSpecLockAnalyzer(opts analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) {
|
||||
return pubSpecLockAnalyzer{
|
||||
logger: log.WithPrefix("pub"),
|
||||
parser: pub.NewParser(),
|
||||
parser: pub.NewParser(opts.DetectionPriority == types.PriorityComprehensive),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ type Option struct {
|
||||
AWSRegion string
|
||||
AWSEndpoint string
|
||||
FileChecksum bool // For SPDX
|
||||
DetectionPriority types.DetectionPriority
|
||||
|
||||
// Git repositories
|
||||
RepoBranch string
|
||||
@@ -50,6 +51,7 @@ func (o *Option) AnalyzerOptions() analyzer.AnalyzerOptions {
|
||||
FilePatterns: o.FilePatterns,
|
||||
Parallel: o.Parallel,
|
||||
DisabledAnalyzers: o.DisabledAnalyzers,
|
||||
DetectionPriority: o.DetectionPriority,
|
||||
MisconfScannerOption: o.MisconfScannerOption,
|
||||
SecretScannerOption: o.SecretScannerOption,
|
||||
LicenseScannerOption: o.LicenseScannerOption,
|
||||
|
||||
10
pkg/fanal/types/detection.go
Normal file
10
pkg/fanal/types/detection.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package types
|
||||
|
||||
// DetectionPriority represents the priority of detection
|
||||
type DetectionPriority string
|
||||
|
||||
// PriorityPrecise tries to minimize false positives
|
||||
const PriorityPrecise DetectionPriority = "precise"
|
||||
|
||||
// PriorityComprehensive tries to minimize false negatives
|
||||
const PriorityComprehensive DetectionPriority = "comprehensive"
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/samber/lo"
|
||||
|
||||
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
xstrings "github.com/aquasecurity/trivy/pkg/x/strings"
|
||||
@@ -96,6 +97,19 @@ var (
|
||||
Default: "https://rekor.sigstore.dev",
|
||||
Usage: "[EXPERIMENTAL] address of rekor STL server",
|
||||
}
|
||||
DetectionPriority = Flag[string]{
|
||||
Name: "detection-priority",
|
||||
ConfigName: "scan.detection-priority",
|
||||
Default: string(ftypes.PriorityPrecise),
|
||||
Values: xstrings.ToStringSlice([]ftypes.DetectionPriority{
|
||||
ftypes.PriorityPrecise,
|
||||
ftypes.PriorityComprehensive,
|
||||
}),
|
||||
Usage: `specify the detection priority:
|
||||
- "precise": Prioritizes precise by minimizing false positives.
|
||||
- "comprehensive": Aims to detect more security findings at the cost of potential false positives.
|
||||
`,
|
||||
}
|
||||
)
|
||||
|
||||
type ScanFlagGroup struct {
|
||||
@@ -108,6 +122,7 @@ type ScanFlagGroup struct {
|
||||
Parallel *Flag[int]
|
||||
SBOMSources *Flag[[]string]
|
||||
RekorURL *Flag[string]
|
||||
DetectionPriority *Flag[string]
|
||||
}
|
||||
|
||||
type ScanOptions struct {
|
||||
@@ -120,6 +135,7 @@ type ScanOptions struct {
|
||||
Parallel int
|
||||
SBOMSources []string
|
||||
RekorURL string
|
||||
DetectionPriority ftypes.DetectionPriority
|
||||
}
|
||||
|
||||
func NewScanFlagGroup() *ScanFlagGroup {
|
||||
@@ -133,6 +149,7 @@ func NewScanFlagGroup() *ScanFlagGroup {
|
||||
SBOMSources: SBOMSourcesFlag.Clone(),
|
||||
RekorURL: RekorURLFlag.Clone(),
|
||||
Slow: SlowFlag.Clone(),
|
||||
DetectionPriority: DetectionPriority.Clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +168,7 @@ func (f *ScanFlagGroup) Flags() []Flagger {
|
||||
f.Parallel,
|
||||
f.SBOMSources,
|
||||
f.RekorURL,
|
||||
f.DetectionPriority,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,5 +198,6 @@ func (f *ScanFlagGroup) ToOptions(args []string) (ScanOptions, error) {
|
||||
Parallel: parallel,
|
||||
SBOMSources: f.SBOMSources.Value(),
|
||||
RekorURL: f.RekorURL.Value(),
|
||||
DetectionPriority: ftypes.DetectionPriority(f.DetectionPriority.Value()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -1,9 +1,100 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
)
|
||||
|
||||
// PkgType represents package type
|
||||
type PkgType = string
|
||||
|
||||
// Scanner represents the type of security scanning
|
||||
type Scanner string
|
||||
|
||||
// Scanners is a slice of scanners
|
||||
type Scanners []Scanner
|
||||
|
||||
const (
|
||||
// PkgTypeUnknown is a package type of unknown
|
||||
PkgTypeUnknown PkgType = "unknown"
|
||||
|
||||
// PkgTypeOS is a package type of OS packages
|
||||
PkgTypeOS PkgType = "os"
|
||||
|
||||
// PkgTypeLibrary is a package type of programming language dependencies
|
||||
PkgTypeLibrary PkgType = "library"
|
||||
|
||||
// UnknownScanner is the scanner of unknown
|
||||
UnknownScanner Scanner = "unknown"
|
||||
|
||||
// NoneScanner is the scanner of none
|
||||
NoneScanner Scanner = "none"
|
||||
|
||||
// SBOMScanner is the virtual scanner of SBOM, which cannot be enabled by the user
|
||||
SBOMScanner Scanner = "sbom"
|
||||
|
||||
// VulnerabilityScanner is the scanner of vulnerabilities
|
||||
VulnerabilityScanner Scanner = "vuln"
|
||||
|
||||
// MisconfigScanner is the scanner of misconfigurations
|
||||
MisconfigScanner Scanner = "misconfig"
|
||||
|
||||
// SecretScanner is the scanner of secrets
|
||||
SecretScanner Scanner = "secret"
|
||||
|
||||
// RBACScanner is the scanner of rbac assessment
|
||||
RBACScanner Scanner = "rbac"
|
||||
|
||||
// LicenseScanner is the scanner of licenses
|
||||
LicenseScanner Scanner = "license"
|
||||
)
|
||||
|
||||
var (
|
||||
PkgTypes = []string{
|
||||
PkgTypeOS,
|
||||
PkgTypeLibrary,
|
||||
}
|
||||
|
||||
AllScanners = Scanners{
|
||||
VulnerabilityScanner,
|
||||
MisconfigScanner,
|
||||
RBACScanner,
|
||||
SecretScanner,
|
||||
LicenseScanner,
|
||||
NoneScanner,
|
||||
}
|
||||
|
||||
// AllImageConfigScanners has a list of available scanners on container image config.
|
||||
// The container image in container registries consists of manifest, config and layers.
|
||||
// Trivy is also able to detect security issues on the image config.
|
||||
AllImageConfigScanners = Scanners{
|
||||
MisconfigScanner,
|
||||
SecretScanner,
|
||||
NoneScanner,
|
||||
}
|
||||
)
|
||||
|
||||
func (scanners *Scanners) Enable(s Scanner) {
|
||||
if !scanners.Enabled(s) {
|
||||
*scanners = append(*scanners, s)
|
||||
}
|
||||
}
|
||||
|
||||
func (scanners *Scanners) Enabled(s Scanner) bool {
|
||||
return slices.Contains(*scanners, s)
|
||||
}
|
||||
|
||||
// AnyEnabled returns true if any of the passed scanners is included.
|
||||
func (scanners *Scanners) AnyEnabled(ss ...Scanner) bool {
|
||||
for _, s := range ss {
|
||||
if scanners.Enabled(s) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ScanTarget holds the attributes for scanning.
|
||||
type ScanTarget struct {
|
||||
Name string // container image name, file path, etc
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"slices"
|
||||
)
|
||||
|
||||
// PkgType represents package type
|
||||
type PkgType = string
|
||||
|
||||
// Scanner represents the type of security scanning
|
||||
type Scanner string
|
||||
|
||||
// Scanners is a slice of scanners
|
||||
type Scanners []Scanner
|
||||
|
||||
const (
|
||||
// PkgTypeUnknown is a package type of unknown
|
||||
PkgTypeUnknown = PkgType("unknown")
|
||||
|
||||
// PkgTypeOS is a package type of OS packages
|
||||
PkgTypeOS = PkgType("os")
|
||||
|
||||
// PkgTypeLibrary is a package type of programming language dependencies
|
||||
PkgTypeLibrary = PkgType("library")
|
||||
|
||||
// UnknownScanner is the scanner of unknown
|
||||
UnknownScanner = Scanner("unknown")
|
||||
|
||||
// NoneScanner is the scanner of none
|
||||
NoneScanner = Scanner("none")
|
||||
|
||||
// SBOMScanner is the virtual scanner of SBOM, which cannot be enabled by the user
|
||||
SBOMScanner = Scanner("sbom")
|
||||
|
||||
// VulnerabilityScanner is the scanner of vulnerabilities
|
||||
VulnerabilityScanner = Scanner("vuln")
|
||||
|
||||
// MisconfigScanner is the scanner of misconfigurations
|
||||
MisconfigScanner = Scanner("misconfig")
|
||||
|
||||
// SecretScanner is the scanner of secrets
|
||||
SecretScanner = Scanner("secret")
|
||||
|
||||
// RBACScanner is the scanner of rbac assessment
|
||||
RBACScanner = Scanner("rbac")
|
||||
|
||||
// LicenseScanner is the scanner of licenses
|
||||
LicenseScanner = Scanner("license")
|
||||
)
|
||||
|
||||
var (
|
||||
PkgTypes = []string{
|
||||
PkgTypeOS,
|
||||
PkgTypeLibrary,
|
||||
}
|
||||
|
||||
AllScanners = Scanners{
|
||||
VulnerabilityScanner,
|
||||
MisconfigScanner,
|
||||
RBACScanner,
|
||||
SecretScanner,
|
||||
LicenseScanner,
|
||||
NoneScanner,
|
||||
}
|
||||
|
||||
// AllImageConfigScanners has a list of available scanners on container image config.
|
||||
// The container image in container registries consists of manifest, config and layers.
|
||||
// Trivy is also able to detect security issues on the image config.
|
||||
AllImageConfigScanners = Scanners{
|
||||
MisconfigScanner,
|
||||
SecretScanner,
|
||||
NoneScanner,
|
||||
}
|
||||
)
|
||||
|
||||
func (scanners *Scanners) Enable(s Scanner) {
|
||||
if !scanners.Enabled(s) {
|
||||
*scanners = append(*scanners, s)
|
||||
}
|
||||
}
|
||||
|
||||
func (scanners *Scanners) Enabled(s Scanner) bool {
|
||||
return slices.Contains(*scanners, s)
|
||||
}
|
||||
|
||||
// AnyEnabled returns true if any of the passed scanners is included.
|
||||
func (scanners *Scanners) AnyEnabled(ss ...Scanner) bool {
|
||||
for _, s := range ss {
|
||||
if scanners.Enabled(s) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user