mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-07 05:10:46 -08:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85e45cad95 | ||
|
|
9fa512a652 | ||
|
|
349371bbc9 | ||
|
|
4446961167 | ||
|
|
04473ada48 | ||
|
|
1b66b77f69 | ||
|
|
8fc6ea6489 | ||
|
|
eaf2da20a6 | ||
|
|
083c157b05 | ||
|
|
e26e39a7f8 | ||
|
|
04e7ccabea | ||
|
|
415e1d8ea3 | ||
|
|
3bb8852ef7 | ||
|
|
c0fddd9467 |
11
.github/workflows/mkdocs-latest.yaml
vendored
11
.github/workflows/mkdocs-latest.yaml
vendored
@@ -1,5 +1,10 @@
|
||||
name: Deploy the latest documentation
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: Version to be deployed
|
||||
required: true
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
@@ -27,7 +32,11 @@ jobs:
|
||||
run: |
|
||||
git config user.name "knqyf263"
|
||||
git config user.email "knqyf263@gmail.com"
|
||||
- name: Deploy the latest documents
|
||||
- name: Deploy the latest documents from new tag push
|
||||
if: ${{ github.event.inputs.version == '' }}
|
||||
run: |
|
||||
VERSION=$(echo ${{ github.ref }} | sed -e "s#refs/tags/##g")
|
||||
mike deploy --push --update-aliases $VERSION latest
|
||||
- name: Deploy the latest documents from manual trigger
|
||||
if: ${{ github.event.inputs.version != '' }}
|
||||
run: mike deploy --push --update-aliases ${{ github.event.inputs.version }} latest
|
||||
|
||||
4
.github/workflows/publish-chart.yaml
vendored
4
.github/workflows/publish-chart.yaml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
continue-on-error: true
|
||||
## Upload the tar in the Releases repository
|
||||
run: |
|
||||
./cr upload -o ${{ env.GH_OWNER }} -r ${{ env.HELM_REP }} --token ${{ secrets.ORG_GITHUB_TOKEN }} -p .cr-release-packages
|
||||
./cr upload -o ${{ env.GH_OWNER }} -r ${{ env.HELM_REP }} --token ${{ secrets.ORG_REPO_TOKEN }} -p .cr-release-packages
|
||||
- name: Index helm chart
|
||||
run: |
|
||||
./cr index -o ${{ env.GH_OWNER }} -r ${{ env.HELM_REP }} -c https://${{ env.GH_OWNER }}.github.io/${{ env.HELM_REP }}/ -i index.yaml
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
- name: Push index file
|
||||
uses: dmnemec/copy_file_to_another_repo_action@v1.0.4
|
||||
env:
|
||||
API_TOKEN_GITHUB: ${{ secrets.ORG_GITHUB_TOKEN }}
|
||||
API_TOKEN_GITHUB: ${{ secrets.ORG_REPO_TOKEN }}
|
||||
with:
|
||||
source_file: 'index.yaml'
|
||||
destination_repo: '${{ env.GH_OWNER }}/${{ env.HELM_REP }}'
|
||||
|
||||
6
.github/workflows/release.yaml
vendored
6
.github/workflows/release.yaml
vendored
@@ -49,7 +49,7 @@ jobs:
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ env.GH_USER }}
|
||||
password: ${{ secrets.ORG_GITHUB_TOKEN }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Login to ECR
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
@@ -62,14 +62,14 @@ jobs:
|
||||
version: v0.164.0
|
||||
args: release --rm-dist
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.ORG_GITHUB_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.ORG_REPO_TOKEN }}
|
||||
- name: Checkout trivy-repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: ${{ github.repository_owner }}/trivy-repo
|
||||
path: trivy-repo
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.ORG_GITHUB_TOKEN }}
|
||||
token: ${{ secrets.ORG_REPO_TOKEN }}
|
||||
- name: Setup git settings
|
||||
run: |
|
||||
git config --global user.email "knqyf263@gmail.com"
|
||||
|
||||
23
.github/workflows/scan.yaml
vendored
Normal file
23
.github/workflows/scan.yaml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: Scan
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
build:
|
||||
name: Scan Go vulnerabilities
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Run Trivy vulnerability scanner to scan for Critical Vulnerabilities
|
||||
uses: aquasecurity/trivy-action@master
|
||||
with:
|
||||
scan-type: 'fs'
|
||||
exit-code: '1'
|
||||
severity: 'CRITICAL'
|
||||
|
||||
- name: Run Trivy vulnerability scanner to scan for Medium and High Vulnerabilities
|
||||
uses: aquasecurity/trivy-action@master
|
||||
with:
|
||||
scan-type: 'fs'
|
||||
exit-code: '0'
|
||||
severity: 'HIGH,MEDIUM'
|
||||
2
.github/workflows/test.yaml
vendored
2
.github/workflows/test.yaml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
- name: Lint
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
version: v1.31
|
||||
version: v1.39
|
||||
args: --deadline=30m
|
||||
|
||||
- name: Run unit tests
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.12
|
||||
FROM alpine:3.13
|
||||
RUN apk --no-cache add ca-certificates git
|
||||
COPY trivy /usr/local/bin/trivy
|
||||
COPY contrib/*.tpl contrib/
|
||||
|
||||
2
Makefile
2
Makefile
@@ -15,7 +15,7 @@ $(GOBIN)/wire:
|
||||
|
||||
.PHONY: wire
|
||||
wire: $(GOBIN)/wire
|
||||
wire gen ./pkg/... ./internal/...
|
||||
wire gen ./pkg/...
|
||||
|
||||
.PHONY: mock
|
||||
mock: $(GOBIN)/mockery
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"version": "0.15.0",
|
||||
"rules": [
|
||||
{{- $t_first := true }}
|
||||
{{- range . }}
|
||||
{{- range $result := . }}
|
||||
{{- $vulnerabilityType := .Type }}
|
||||
{{- range .Vulnerabilities -}}
|
||||
{{- if $t_first -}}
|
||||
@@ -20,7 +20,7 @@
|
||||
,
|
||||
{{- end }}
|
||||
{
|
||||
"id": "{{ .VulnerabilityID }}/{{ .PkgName }}",
|
||||
"id": {{ printf "%s: %s-%s %s" $result.Target .PkgName .InstalledVersion .VulnerabilityID | toJson }},
|
||||
"name": "{{ toSarifRuleName $vulnerabilityType }}",
|
||||
"shortDescription": {
|
||||
"text": {{ printf "%v Package: %v" .VulnerabilityID .PkgName | printf "%q" }}
|
||||
@@ -57,7 +57,7 @@
|
||||
},
|
||||
"results": [
|
||||
{{- $t_first := true }}
|
||||
{{- range . }}
|
||||
{{- range $result := . }}
|
||||
{{- $filePath := .Target }}
|
||||
{{- range $index, $vulnerability := .Vulnerabilities -}}
|
||||
{{- if $t_first -}}
|
||||
@@ -66,7 +66,7 @@
|
||||
,
|
||||
{{- end }}
|
||||
{
|
||||
"ruleId": "{{ $vulnerability.VulnerabilityID }}/{{ $vulnerability.PkgName }}",
|
||||
"ruleId": {{ printf "%s: %s-%s %s" $result.Target .PkgName .InstalledVersion .VulnerabilityID | toJson }},
|
||||
"ruleIndex": {{ $index }},
|
||||
"level": "{{ toSarifErrorLevel $vulnerability.Vulnerability.Severity }}",
|
||||
"message": {
|
||||
@@ -92,4 +92,4 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ container_scanning:
|
||||
--output "$CI_PROJECT_DIR/gl-container-scanning-report.json" "$FULL_IMAGE_NAME"
|
||||
# Prints full report
|
||||
- time trivy --exit-code 0 --cache-dir .trivycache/ --no-progress "$FULL_IMAGE_NAME"
|
||||
# Fails on high and critical vulnerabilities
|
||||
# Fail on critical vulnerabilities
|
||||
- time trivy --exit-code 1 --cache-dir .trivycache/ --severity CRITICAL --no-progress "$FULL_IMAGE_NAME"
|
||||
cache:
|
||||
paths:
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
- PHP
|
||||
- composer.lock
|
||||
- Node.js
|
||||
- package-lock.json
|
||||
- package-lock.json (dev dependencies are excluded)
|
||||
- yarn.lock
|
||||
- Rust
|
||||
- Cargo.lock
|
||||
@@ -19,7 +19,8 @@
|
||||
- Java
|
||||
- JAR/WAR/EAR files (*.jar, *.war, and *.ear)
|
||||
- Go
|
||||
- Binaries built by Go
|
||||
- Binaries built by Go (UPX-compressed binaries don't work)
|
||||
- go.sum
|
||||
|
||||
The path of these files does not matter.
|
||||
|
||||
|
||||
11
go.mod
11
go.mod
@@ -5,9 +5,10 @@ go 1.16
|
||||
require (
|
||||
github.com/Masterminds/goutils v1.1.0 // indirect
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible
|
||||
github.com/apache/thrift v0.14.0 // indirect
|
||||
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986
|
||||
github.com/aquasecurity/fanal v0.0.0-20210430044351-34b55f31bc70
|
||||
github.com/aquasecurity/go-dep-parser v0.0.0-20210427143403-3c97ccc53976
|
||||
github.com/aquasecurity/fanal v0.0.0-20210520034323-54c5a82e861f
|
||||
github.com/aquasecurity/go-dep-parser v0.0.0-20210520015931-0dd56983cc62
|
||||
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce
|
||||
github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798
|
||||
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
|
||||
@@ -16,6 +17,7 @@ require (
|
||||
github.com/caarlos0/env/v6 v6.0.0
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible
|
||||
github.com/cheggaaa/pb/v3 v3.0.3
|
||||
github.com/containerd/containerd v1.4.4 // indirect
|
||||
github.com/docker/docker v20.10.3+incompatible
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/elazarl/goproxy v0.0.0-20200809112317-0581fc3aee2d // indirect
|
||||
@@ -23,7 +25,7 @@ require (
|
||||
github.com/goccy/go-yaml v1.8.2 // indirect
|
||||
github.com/golang/protobuf v1.4.3
|
||||
github.com/google/go-containerregistry v0.1.2
|
||||
github.com/google/go-github/v28 v28.1.1
|
||||
github.com/google/go-github/v33 v33.0.0
|
||||
github.com/google/wire v0.4.0
|
||||
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
|
||||
github.com/hashicorp/go-getter v1.5.2
|
||||
@@ -45,11 +47,14 @@ require (
|
||||
github.com/twitchtv/twirp v5.10.1+incompatible
|
||||
github.com/urfave/cli/v2 v2.3.0
|
||||
go.uber.org/zap v1.16.0
|
||||
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
|
||||
google.golang.org/protobuf v1.25.0
|
||||
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
|
||||
gopkg.in/go-playground/validator.v9 v9.31.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
k8s.io/kubernetes v1.13.5 // indirect
|
||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920
|
||||
)
|
||||
|
||||
37
go.sum
37
go.sum
@@ -141,6 +141,7 @@ github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdc
|
||||
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
|
||||
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||
@@ -160,20 +161,24 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.14.0 h1:vqZ2DP42i8th2OsgCcYZkirtbzvpZEFx53LiWDJXIAs=
|
||||
github.com/apache/thrift v0.14.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apex/log v1.1.4/go.mod h1:AlpoD9aScyQfJDVHmLMEcx4oU6LqzkWp4Mg9GdAcEvQ=
|
||||
github.com/apex/log v1.3.0/go.mod h1:jd8Vpsr46WAe3EZSQ/IUMs2qQD/GOycT5rPWCO1yGcs=
|
||||
github.com/apex/logs v0.0.4/go.mod h1:XzxuLZ5myVHDy9SAmYpamKKRNApGj54PfYLcFrXqDwo=
|
||||
github.com/aphistic/golf v0.0.0-20180712155816-02c07f170c5a/go.mod h1:3NqKYiepwy8kCu4PNA+aP7WUV72eXWJeP9/r3/K9aLE=
|
||||
github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3stzu0Xys=
|
||||
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
|
||||
github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
|
||||
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
|
||||
github.com/apparentlymart/go-textseg/v12 v12.0.0 h1:bNEQyAGak9tojivJNkoqWErVCQbjdL7GzRt3F8NvfJ0=
|
||||
github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
|
||||
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986 h1:2a30xLN2sUZcMXl50hg+PJCIDdJgIvIbVcKqLJ/ZrtM=
|
||||
github.com/aquasecurity/bolt-fixtures v0.0.0-20200903104109-d34e7f983986/go.mod h1:NT+jyeCzXk6vXR5MTkdn4z64TgGfE5HMLC8qfj5unl8=
|
||||
github.com/aquasecurity/fanal v0.0.0-20210430044351-34b55f31bc70 h1:0v2pf+hIElPvGKLyDye08UtPH8AjujTVLUOWOgoM5O4=
|
||||
github.com/aquasecurity/fanal v0.0.0-20210430044351-34b55f31bc70/go.mod h1:cPTOJcf8bdP24oXhBsPVVczcjkICcApAwAOsn6CpHTI=
|
||||
github.com/aquasecurity/go-dep-parser v0.0.0-20210427143403-3c97ccc53976 h1:ypl/IDxujzEymmwtzGJqQyboI2oZr1se+OoYaGqgBzQ=
|
||||
github.com/aquasecurity/go-dep-parser v0.0.0-20210427143403-3c97ccc53976/go.mod h1:Cv/FOCXy6gwvDbz/KX48+y//SmbnKroFwW5hquXn5G4=
|
||||
github.com/aquasecurity/fanal v0.0.0-20210520034323-54c5a82e861f h1:gR7Dh0VyyK1z4L2qPmUFw7/6F4ebshVMybsfNdZwbC8=
|
||||
github.com/aquasecurity/fanal v0.0.0-20210520034323-54c5a82e861f/go.mod h1:s4rJj8D45R28N3PNz5+hpjSHzD3YhaIbYrQtuYciGdY=
|
||||
github.com/aquasecurity/go-dep-parser v0.0.0-20210520015931-0dd56983cc62 h1:aahEMQZXrwhpCMlDgXi2d7jJVNDTpYGJOgLyNptGQoY=
|
||||
github.com/aquasecurity/go-dep-parser v0.0.0-20210520015931-0dd56983cc62/go.mod h1:Cv/FOCXy6gwvDbz/KX48+y//SmbnKroFwW5hquXn5G4=
|
||||
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce h1:QgBRgJvtEOBtUXilDb1MLi1p1MWoyFDXAu5DEUl5nwM=
|
||||
github.com/aquasecurity/go-gem-version v0.0.0-20201115065557-8eed6fe000ce/go.mod h1:HXgVzOPvXhVGLJs4ZKO817idqr/xhwsTcj17CLYY74s=
|
||||
github.com/aquasecurity/go-npm-version v0.0.0-20201110091526-0b796d180798 h1:eveqE9ivrt30CJ7dOajOfBavhZ4zPqHcZe/4tKp0alc=
|
||||
@@ -288,8 +293,9 @@ github.com/containerd/containerd v1.3.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMX
|
||||
github.com/containerd/containerd v1.4.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.4.1-0.20201117152358-0edc412565dc/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.4.3 h1:ijQT13JedHSHrQGWFcGEwzcNKrAGIiZ+jSD5QQG07SY=
|
||||
github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/containerd v1.4.4 h1:rtRG4N6Ct7GNssATwgpvMGfnjnwfjnu/Zs9W3Ikzq+M=
|
||||
github.com/containerd/containerd v1.4.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY=
|
||||
github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo=
|
||||
@@ -305,6 +311,7 @@ github.com/containerd/stargz-snapshotter v0.0.0-20201027054423-3a04e4c2c116/go.m
|
||||
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
|
||||
github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
|
||||
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
|
||||
github.com/containerd/typeurl v1.0.1 h1:PvuK4E3D5S5q6IqsPDCy928FhP0LUIGcmZ/Yhgp5Djw=
|
||||
github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=
|
||||
github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
@@ -477,6 +484,7 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
|
||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
|
||||
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
|
||||
github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
|
||||
@@ -586,8 +594,9 @@ github.com/google/go-containerregistry v0.1.2 h1:YjFNKqxzWUVZND8d4ItF9wuYlE75WQf
|
||||
github.com/google/go-containerregistry v0.1.2/go.mod h1:GPivBPgdAyd2SU+vf6EpsgOtWDuPqjW0hJZt4rNdTZ4=
|
||||
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
|
||||
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
||||
github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo=
|
||||
github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
|
||||
github.com/google/go-github/v33 v33.0.0 h1:qAf9yP0qc54ufQxzwv+u9H0tiVOnPJxo0lI/JXqw3ZM=
|
||||
github.com/google/go-github/v33 v33.0.0/go.mod h1:GMdDnVZY/2TsWgp/lkYnpSAh6TrzhANBBwm6k6TTEXg=
|
||||
github.com/google/go-jsonnet v0.17.0/go.mod h1:sOcuej3UW1vpPTZOr8L7RQimqai1a57bt5j22LzGZCw=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
@@ -707,7 +716,9 @@ github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/hcl/v2 v2.6.0 h1:3krZOfGY6SziUXa6H9PJU6TyohHn7I+ARYnhbeNBz+o=
|
||||
github.com/hashicorp/hcl/v2 v2.6.0/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
@@ -787,6 +798,7 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxv
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
@@ -865,6 +877,7 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
|
||||
github.com/mitchellh/go-ps v0.0.0-20190716172923-621e5597135b/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
|
||||
@@ -872,9 +885,12 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
|
||||
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
|
||||
github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE=
|
||||
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/moby/buildkit v0.8.1 h1:zrGxLwffKM8nVxBvaJa7H404eQLfqlg1GB6YVIzXVQ0=
|
||||
github.com/moby/buildkit v0.8.1/go.mod h1:/kyU1hKy/aYCuP39GZA9MaKioovHku57N6cqlKZIaiQ=
|
||||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||
github.com/moby/sys/mount v0.1.0/go.mod h1:FVQFLDRWwyBjDTBNQXDlWnSFREqOo3OKX9aqhmeoo74=
|
||||
@@ -947,6 +963,7 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
|
||||
github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
|
||||
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/open-policy-agent/conftest v0.23.0 h1:i/cmUjNKDz973vR1cm+x3DqTei/jBPosPvjeot6+p9M=
|
||||
github.com/open-policy-agent/conftest v0.23.0/go.mod h1:NA6+vKd93pb04H9jiV3WRGJKLj/pzYdQg7XCdoPPUDI=
|
||||
github.com/open-policy-agent/opa v0.25.2 h1:zTQuUMvB5xkYixKB9LFVbUd7DcUt1jfS0QKTo+/Vfyc=
|
||||
github.com/open-policy-agent/opa v0.25.2/go.mod h1:iGThTRECCfKQKICueOZkXUi0opN7BR3qiAnIrNHCmlI=
|
||||
@@ -1173,6 +1190,7 @@ github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPf
|
||||
github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmccombs/hcl2json v0.3.1 h1:Pf+Lb9OpZ5lkQuIC0BB5txdCQskZ2ud/l8sz/Nkjf3A=
|
||||
github.com/tmccombs/hcl2json v0.3.1/go.mod h1:ljY0/prd2IFUF3cagQjV3cpPEEQKzqyGqnKI7m5DBVY=
|
||||
github.com/tommy-muehle/go-mnd v1.1.1/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig=
|
||||
github.com/tommy-muehle/go-mnd v1.3.1-0.20200224220436-e6f9a994e8fa/go.mod h1:dSUh0FtTP8VhvkL1S+gUR1OKd9ZnSaozuI6r3m6wOig=
|
||||
@@ -1197,7 +1215,6 @@ github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX
|
||||
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
|
||||
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
|
||||
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/uudashr/gocognit v1.0.1/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM=
|
||||
@@ -1239,6 +1256,7 @@ github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX
|
||||
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
|
||||
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
|
||||
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
|
||||
github.com/zclconf/go-cty v1.6.1 h1:wHtZ+LSSQVwUSb+XIJ5E9hgAQxyWATZsAWT+ESJ9dQ0=
|
||||
github.com/zclconf/go-cty v1.6.1/go.mod h1:VDR4+I79ubFBGm1uJac1226K5yANQFHeauxPBoP54+o=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
@@ -1298,8 +1316,9 @@ golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9 h1:phUcVbl53swtrUN8kQEXFhUxPlIlWyBfKmidCu7P95o=
|
||||
golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f h1:aZp0e2vLN4MToVqnjNEYEtrEA8RH8U8FN1CU7JgqsPU=
|
||||
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -1809,6 +1828,8 @@ k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKf
|
||||
k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
|
||||
k8s.io/kubernetes v1.11.10/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
k8s.io/kubernetes v1.13.5 h1:nI5TsYNPpcXnPPptVx+OpoXdAckpq6omYLkDZy/WYuE=
|
||||
k8s.io/kubernetes v1.13.5/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
k8s.io/legacy-cloud-providers v0.17.4/go.mod h1:FikRNoD64ECjkxO36gkDgJeiQWwyZTuBkhu+yxOc1Js=
|
||||
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
|
||||
k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC+jCCAeKgAwIBAgIRAJLJ5vw48YZwoHlC8i6VdHswDQYJKoZIhvcNAQELBQAw
|
||||
EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0yMDA1MDMxMTU2MzhaFw0yMTA1MDMxMTU2
|
||||
MzhaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
|
||||
ggEKAoIBAQDhnepAL1Atd1xVh/TOZpTK7yHwtOrtGWNEqNkFbcyD7x9CNgUkxjO8
|
||||
nc4ynEo4ARpLj+2VDLIwi93weCFj6mcz2tdHi7n0eiPR7+PSNMNpPFwablLOEtaX
|
||||
XVqHhJNsHcJx6okX6ullksJoRnZGu+n1LvGRMMLWjS3UJZA6+1pujoifyrx9YXLU
|
||||
qSjkRRv3Ly8HmAPJq0T19uCZiJ8qbrW1Vx3hdUILL4OlJmpjZvGKMRnolinko2Vk
|
||||
0pHH5MWz0iUbqWQjHZmQWi0rDHRAFbuCqQdmFsEneXmUzExXZbyHwrTH/mrjJTCJ
|
||||
YmtR7Eq80AxsWnXNI3Z0mVQ9/nZDsT31AgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF
|
||||
oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC
|
||||
CWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAG7spAmpZVKOV913WhKZp8o1+
|
||||
T6v/b6KUOAZ0iWXeGWO7LwnRaulkyauGav9xJUpfEw7Z/57qwTVIGZD6mZF6tt77
|
||||
FsXXiQJA88LrQnt1BRTeNK8sRp4S3N1hrtY3akTit/dyQcfh3NSDttzkYsoUu0qT
|
||||
DUkXD0b4eDmaD47+0Z6eIVp3aEcPMzpiy6qWc5fMjMeHjtYF4lBSF0JTWzmxNUGl
|
||||
fiGhMJStQK/n73t58O7h5Adva5wRV+Km6pa+6SfOxPNUjsxXjG0LzWA9dJg/q2rs
|
||||
k/ouIE05BfB3z538ncQVBTwfPMClbIiJhAs3b6ej22+j/O+vbFBmdfkpVpFRtg==
|
||||
MIICwTCCAamgAwIBAgIJAP09YW8ChPlwMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV
|
||||
BAoMB0FjbWUgQ28wIBcNMjEwNTEyMDQ0NzA1WhgPMjEwMDA0MTQwNDQ3MDVaMBIx
|
||||
EDAOBgNVBAoMB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||
AQDNmKpDOzU8GK5Xb3GfeqU1kKQ0gBejGtqK5ydH8tlRoy2NKGvjJ95nhIxUXMKe
|
||||
e345JFlzkCen5Ekvt70LT0O253z0FecfpaFilreIiu5J2YWWNtlruMhpjp4kYVMO
|
||||
piKnujiNK9eAUcz++YeAmrog7QPBJBCgdu18xTy/yOW/Y414e1efvbRJZ4TaQb0Y
|
||||
LgXRl1nlOLPPr5ew9pgnct7DxJVXpjXtgBxCsfcjH4kZGfc9zP0IKyODqaSCFRtj
|
||||
eKH8gSpJCimBp3hpWvsSTHTRraOxAGXqhIYPhqRM83eB2QbeHnyk+YOn76pdMndb
|
||||
vqAPksmTyHcgZShkhGcHKvbVAgMBAAGjGDAWMBQGA1UdEQQNMAuCCWxvY2FsaG9z
|
||||
dDANBgkqhkiG9w0BAQsFAAOCAQEAHxXOTKGP1hl3J2jQrpha5LuYdMEbK1HFbPhV
|
||||
042k0tBmfP3wRgx0o/WQhg4f5RswQRtipdUCmMZVOAoQfos8j9LFmIKwcsboEQe/
|
||||
Fvqq2+W/5TRhsKn/1OxvCZAEurazSygtm6hyiMGwKjJLfyzwjZx+Oopn3lqRUP36
|
||||
gLQQ57szoNZFKyPN2z2unXAuDG5wpG2InX8WJvlrhaiCHGUoxO8r0rVawm58bahM
|
||||
uGPlVPCNdxl1h7K8aecKpm+7Wh8n06Nl/kOWBDFAXeI8IwrnIy1rAZLngvnjqL//
|
||||
umjXKCBWya48ed9HMoOR2aruzseXc8k6cGXuBxYFtHissPvPPQ==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDhnepAL1Atd1xV
|
||||
h/TOZpTK7yHwtOrtGWNEqNkFbcyD7x9CNgUkxjO8nc4ynEo4ARpLj+2VDLIwi93w
|
||||
eCFj6mcz2tdHi7n0eiPR7+PSNMNpPFwablLOEtaXXVqHhJNsHcJx6okX6ullksJo
|
||||
RnZGu+n1LvGRMMLWjS3UJZA6+1pujoifyrx9YXLUqSjkRRv3Ly8HmAPJq0T19uCZ
|
||||
iJ8qbrW1Vx3hdUILL4OlJmpjZvGKMRnolinko2Vk0pHH5MWz0iUbqWQjHZmQWi0r
|
||||
DHRAFbuCqQdmFsEneXmUzExXZbyHwrTH/mrjJTCJYmtR7Eq80AxsWnXNI3Z0mVQ9
|
||||
/nZDsT31AgMBAAECggEBAKwwGhSMR3O7sdNxJIvVzF8orE2JtfXoN1OyTZcQGlLi
|
||||
z4d3tOtA/UFJapJDp30gklHy8Y6clu3oASVCebFItyTjMwPehrgn82iI3eWS8URC
|
||||
lcRySG4QAIia7bmZm+2atMi+B40icqhbnlV42VHYnpDKGAEIJtsZ+kz7shzhsj3G
|
||||
yTQMFyuqk0DUmsbSVKPjryv15DXsT9Rk2pVZYFhiRw/gQpWD58GMP/HMrSz+sjuX
|
||||
ZIlhSMGVWA4Yc7le4PpWI2qAZLR+X1EgkzxcMJ0kWvnvzEXFmofaYzkbEcNOlguF
|
||||
Bv9kP5fh35AbQbTLykGO9h4VrfDajlHequzNBJs1z60CgYEA53cwBh42pg/fSmaO
|
||||
sowpFV52ZfbfUPcuXRuaidHWougByB8P8XTMeQTse4NLt+2oat/5rdP3keGr5OR5
|
||||
8q7v8/R/KY0NQOa/93BUeRDW4ntxMECWbC2p/sq2wnRKTl+yepAWrRXzk8z9vFP/
|
||||
TZM5m65aj3IsZ3Bo1WG+SSf8bvsCgYEA+YgFxmiTauKRO4IVPuOqJ88yC5SQ83mF
|
||||
T54ILYalG3yq/Jm1TTOzoZAoKvHrJeeZqQvjS4jSY5gc5TCrUVTdsw5nXtrRKZJs
|
||||
HjtVT78qfzjCSHzImvc3Rw5+SNO2+j9yxuBSAG4tEKD3KKxSodXnKtD4CwzvRdyI
|
||||
gUyjQi3Os88CgYEAgrzegkYkhe2nKKX+6bijJ+/AHl2vy1KifHKv+jJs8nzrLLbm
|
||||
0XIwYBa44BbL+Oqi2yMBKv7z8hEuf03R15KZ9Ahgnv6Nwt/TBBcNj4hEZ45j42ZH
|
||||
0HiGcWTcj78RjW0eKX4jYMZqW0xI8Uvcg1uqCVYUzrsle5ORkxzvVvDf82sCgYEA
|
||||
4hS9tsA1IJhaoaIAgdRf7GWroBZhJlep0zMJkcX2fer8OJVDUMlRLUahPhelx9gI
|
||||
vsLIkz1J8XZ2Z6kq7yuHGp4oRibXb2T8lH+JkhFP/ah9TpPQZacq7DRTcsRvelhW
|
||||
M542bbFlHzXX+X/39i0Jnx9qPQjhGVjwTMYU/Pbn2r0CgYAkwwh2oq6BP3w7/4xe
|
||||
giaW/5zzMA8R9ZXFfkE4OXk2vig8LzUn1IO0JeGLyHQbdg8exYxTU3zygIlSvCQP
|
||||
Zbl0+RB+NHwGOqlpEDdPFXsqi1GAdWOC6FzYtBFNk9WyjFICXjB42wnfIIUQPLU1
|
||||
kQKFaehfx9KR0iW1dnm3vbFlzw==
|
||||
-----END PRIVATE KEY-----
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAzZiqQzs1PBiuV29xn3qlNZCkNIAXoxraiucnR/LZUaMtjShr
|
||||
4yfeZ4SMVFzCnnt+OSRZc5Anp+RJL7e9C09Dtud89BXnH6WhYpa3iIruSdmFljbZ
|
||||
a7jIaY6eJGFTDqYip7o4jSvXgFHM/vmHgJq6IO0DwSQQoHbtfMU8v8jlv2ONeHtX
|
||||
n720SWeE2kG9GC4F0ZdZ5Tizz6+XsPaYJ3Lew8SVV6Y17YAcQrH3Ix+JGRn3Pcz9
|
||||
CCsjg6mkghUbY3ih/IEqSQopgad4aVr7Ekx00a2jsQBl6oSGD4akTPN3gdkG3h58
|
||||
pPmDp++qXTJ3W76gD5LJk8h3IGUoZIRnByr21QIDAQABAoIBAQCVN2ETjIxVgqA+
|
||||
K08u7Ses2b6jr/f31AybVasnx/S8EI+F7Llo003SmdvzeqNxvLVeqagWfKCbdM89
|
||||
R8B3zd6aiCYjTSZCzMZ1tGeePR83EB2paUOhsCocmnricpSChEeQrlJO+2vb4QLE
|
||||
Z7xVtXazYPIhophCri4tKUWu+BLvNPez+TndaE5Xg77HLmu24rloZh6XhYDdFWd+
|
||||
u/eF+QiWy4/EoLUv2TLym8ivUws+r2G9yK57kcQCJw+BqlaRew7Ts0RHnam53OxV
|
||||
T4dEHJxAfXO8jC1F5NCjoBO/+0HJqrMtD0NqWH9G+fEtakL7h5oeh6vYrSQfpZGC
|
||||
V7MXojqdAoGBAOhwBi0erXOn4strtkGvSjJ6HVLwWfmm+rlfm23JGigYghYTSxBM
|
||||
ESuwppt1QPXK5jfil89RqrvDqKG9BjXV4yWyaJlIRaYeJe8/TZa3e8WkLr0XaKGH
|
||||
v1LTW+/uc73ihDJ/M2axmP4vjThCfqiG9aKXLCDM+DIgfdvIbkXUfPZjAoGBAOJw
|
||||
Fc6D3z0r09F3/UgtADhQlbD2jzs6xdcqCu7af3527F6ePXXU8CTLS5jusAiDW5xH
|
||||
ukQS/0ZM92UTUJxpQxgzHSWOImhcv3o45vzQ0C0pXSSaE+Pp8QYWaE/BdE+VoVOK
|
||||
YGAfppZywPGnKYt4R5ho5XLwAL3rrH+2m7z51mdnAoGBANP5LbjCLF64Mb1f2pOm
|
||||
f1zvPoTfyr5BSI/7n+yMJL2CNEhbie4v4MzeSeKmGPrO8grvK5EXIkQgGE5/6wT3
|
||||
rTI4tOltHo9zGRdJvMGBTXAd3b32diYxfQrU1BhIducph3PhyweRWTweM4SmJ4ob
|
||||
ojGH+edj5ckZFo50CBTIxrmPAoGBAI8SpSSsfCRJiffjadzt2iK7AColT9DrzM+r
|
||||
1+adlksQ1z7dmxXVqrqE3UpPHljyrrKrO40Bt9vyi6qIrrl1ZRhoS3VMPn9UgwO1
|
||||
6nU5dx/h7+FNnV23ljvzcotaP6R9dca0OzrhJMAQ18qYhY6DPDGXrcqWzNEzlPXJ
|
||||
KtQXxBnnAoGAFQPvW/wDahrGcm1MBw83E0TgNJpoB10tz6R1dLdKVSHJUXMfxmij
|
||||
Wj4MaF0JB0GWRRjutng+i7y7Tx+mUpu80qV8E9zAH7jGFnpqjw8A9zp5ftK00e7Y
|
||||
shRlg+lhJhlvMA5QCYNzpYj+7EXJm7nzbhC6pMtBapT9a/MUPYFte38=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
|
||||
34
integration/testdata/alpine-310.sarif.golden
vendored
34
integration/testdata/alpine-310.sarif.golden
vendored
@@ -11,7 +11,7 @@
|
||||
"version": "0.15.0",
|
||||
"rules": [
|
||||
{
|
||||
"id": "CVE-2019-1549/libcrypto1.1",
|
||||
"id": "testdata/fixtures/alpine-310.tar.gz (alpine 3.10.2): libcrypto1.1-1.1.1c-r0 CVE-2019-1549",
|
||||
"name": "OS Package Vulnerability (Alpine)",
|
||||
"shortDescription": {
|
||||
"text": "CVE-2019-1549 Package: libcrypto1.1"
|
||||
@@ -37,7 +37,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CVE-2019-1551/libcrypto1.1",
|
||||
"id": "testdata/fixtures/alpine-310.tar.gz (alpine 3.10.2): libcrypto1.1-1.1.1c-r0 CVE-2019-1551",
|
||||
"name": "OS Package Vulnerability (Alpine)",
|
||||
"shortDescription": {
|
||||
"text": "CVE-2019-1551 Package: libcrypto1.1"
|
||||
@@ -63,7 +63,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CVE-2019-1563/libcrypto1.1",
|
||||
"id": "testdata/fixtures/alpine-310.tar.gz (alpine 3.10.2): libcrypto1.1-1.1.1c-r0 CVE-2019-1563",
|
||||
"name": "OS Package Vulnerability (Alpine)",
|
||||
"shortDescription": {
|
||||
"text": "CVE-2019-1563 Package: libcrypto1.1"
|
||||
@@ -89,7 +89,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CVE-2019-1547/libcrypto1.1",
|
||||
"id": "testdata/fixtures/alpine-310.tar.gz (alpine 3.10.2): libcrypto1.1-1.1.1c-r0 CVE-2019-1547",
|
||||
"name": "OS Package Vulnerability (Alpine)",
|
||||
"shortDescription": {
|
||||
"text": "CVE-2019-1547 Package: libcrypto1.1"
|
||||
@@ -115,7 +115,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CVE-2019-1549/libssl1.1",
|
||||
"id": "testdata/fixtures/alpine-310.tar.gz (alpine 3.10.2): libssl1.1-1.1.1c-r0 CVE-2019-1549",
|
||||
"name": "OS Package Vulnerability (Alpine)",
|
||||
"shortDescription": {
|
||||
"text": "CVE-2019-1549 Package: libssl1.1"
|
||||
@@ -141,7 +141,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CVE-2019-1551/libssl1.1",
|
||||
"id": "testdata/fixtures/alpine-310.tar.gz (alpine 3.10.2): libssl1.1-1.1.1c-r0 CVE-2019-1551",
|
||||
"name": "OS Package Vulnerability (Alpine)",
|
||||
"shortDescription": {
|
||||
"text": "CVE-2019-1551 Package: libssl1.1"
|
||||
@@ -167,7 +167,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CVE-2019-1563/libssl1.1",
|
||||
"id": "testdata/fixtures/alpine-310.tar.gz (alpine 3.10.2): libssl1.1-1.1.1c-r0 CVE-2019-1563",
|
||||
"name": "OS Package Vulnerability (Alpine)",
|
||||
"shortDescription": {
|
||||
"text": "CVE-2019-1563 Package: libssl1.1"
|
||||
@@ -193,7 +193,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "CVE-2019-1547/libssl1.1",
|
||||
"id": "testdata/fixtures/alpine-310.tar.gz (alpine 3.10.2): libssl1.1-1.1.1c-r0 CVE-2019-1547",
|
||||
"name": "OS Package Vulnerability (Alpine)",
|
||||
"shortDescription": {
|
||||
"text": "CVE-2019-1547 Package: libssl1.1"
|
||||
@@ -222,7 +222,7 @@
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"ruleId": "CVE-2019-1549/libcrypto1.1",
|
||||
"ruleId": "testdata/fixtures/alpine-310.tar.gz (alpine 3.10.2): libcrypto1.1-1.1.1c-r0 CVE-2019-1549",
|
||||
"ruleIndex": 0,
|
||||
"level": "warning",
|
||||
"message": {
|
||||
@@ -238,7 +238,7 @@
|
||||
}]
|
||||
},
|
||||
{
|
||||
"ruleId": "CVE-2019-1551/libcrypto1.1",
|
||||
"ruleId": "testdata/fixtures/alpine-310.tar.gz (alpine 3.10.2): libcrypto1.1-1.1.1c-r0 CVE-2019-1551",
|
||||
"ruleIndex": 1,
|
||||
"level": "warning",
|
||||
"message": {
|
||||
@@ -254,7 +254,7 @@
|
||||
}]
|
||||
},
|
||||
{
|
||||
"ruleId": "CVE-2019-1563/libcrypto1.1",
|
||||
"ruleId": "testdata/fixtures/alpine-310.tar.gz (alpine 3.10.2): libcrypto1.1-1.1.1c-r0 CVE-2019-1563",
|
||||
"ruleIndex": 2,
|
||||
"level": "warning",
|
||||
"message": {
|
||||
@@ -270,7 +270,7 @@
|
||||
}]
|
||||
},
|
||||
{
|
||||
"ruleId": "CVE-2019-1547/libcrypto1.1",
|
||||
"ruleId": "testdata/fixtures/alpine-310.tar.gz (alpine 3.10.2): libcrypto1.1-1.1.1c-r0 CVE-2019-1547",
|
||||
"ruleIndex": 3,
|
||||
"level": "note",
|
||||
"message": {
|
||||
@@ -286,7 +286,7 @@
|
||||
}]
|
||||
},
|
||||
{
|
||||
"ruleId": "CVE-2019-1549/libssl1.1",
|
||||
"ruleId": "testdata/fixtures/alpine-310.tar.gz (alpine 3.10.2): libssl1.1-1.1.1c-r0 CVE-2019-1549",
|
||||
"ruleIndex": 4,
|
||||
"level": "warning",
|
||||
"message": {
|
||||
@@ -302,7 +302,7 @@
|
||||
}]
|
||||
},
|
||||
{
|
||||
"ruleId": "CVE-2019-1551/libssl1.1",
|
||||
"ruleId": "testdata/fixtures/alpine-310.tar.gz (alpine 3.10.2): libssl1.1-1.1.1c-r0 CVE-2019-1551",
|
||||
"ruleIndex": 5,
|
||||
"level": "warning",
|
||||
"message": {
|
||||
@@ -318,7 +318,7 @@
|
||||
}]
|
||||
},
|
||||
{
|
||||
"ruleId": "CVE-2019-1563/libssl1.1",
|
||||
"ruleId": "testdata/fixtures/alpine-310.tar.gz (alpine 3.10.2): libssl1.1-1.1.1c-r0 CVE-2019-1563",
|
||||
"ruleIndex": 6,
|
||||
"level": "warning",
|
||||
"message": {
|
||||
@@ -334,7 +334,7 @@
|
||||
}]
|
||||
},
|
||||
{
|
||||
"ruleId": "CVE-2019-1547/libssl1.1",
|
||||
"ruleId": "testdata/fixtures/alpine-310.tar.gz (alpine 3.10.2): libssl1.1-1.1.1c-r0 CVE-2019-1547",
|
||||
"ruleIndex": 7,
|
||||
"level": "note",
|
||||
"message": {
|
||||
@@ -357,4 +357,4 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
[
|
||||
{
|
||||
"Target": "testdata/fixtures/opensuse-leap-423.tar.gz (opensuse.leap 42.3)",
|
||||
"Type": "opensuse.leap",
|
||||
"Vulnerabilities": null
|
||||
"Type": "opensuse.leap"
|
||||
}
|
||||
]
|
||||
8
pkg/cache/remote.go
vendored
8
pkg/cache/remote.go
vendored
@@ -30,8 +30,8 @@ func NewRemoteCache(url RemoteURL, customHeaders http.Header) cache.ArtifactCach
|
||||
}
|
||||
|
||||
// PutArtifact sends artifact to remote client
|
||||
func (c RemoteCache) PutArtifact(imageID string, imageInfo types.ArtifactInfo) error {
|
||||
_, err := c.client.PutArtifact(c.ctx, rpc.ConvertToRPCArtifactInfo(imageID, imageInfo))
|
||||
func (c RemoteCache) PutArtifact(imageID string, artifactInfo types.ArtifactInfo) error {
|
||||
_, err := c.client.PutArtifact(c.ctx, rpc.ConvertToRPCArtifactInfo(imageID, artifactInfo))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("unable to store cache on the server: %w", err)
|
||||
}
|
||||
@@ -39,8 +39,8 @@ func (c RemoteCache) PutArtifact(imageID string, imageInfo types.ArtifactInfo) e
|
||||
}
|
||||
|
||||
// PutBlob sends blobInfo to remote client
|
||||
func (c RemoteCache) PutBlob(diffID string, layerInfo types.BlobInfo) error {
|
||||
_, err := c.client.PutBlob(c.ctx, rpc.ConvertToRPCBlobInfo(diffID, layerInfo))
|
||||
func (c RemoteCache) PutBlob(diffID string, blobInfo types.BlobInfo) error {
|
||||
_, err := c.client.PutBlob(c.ctx, rpc.ConvertToRPCBlobInfo(diffID, blobInfo))
|
||||
if err != nil {
|
||||
return xerrors.Errorf("unable to store cache on the server: %w", err)
|
||||
}
|
||||
|
||||
@@ -12,12 +12,13 @@ import (
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/aquasecurity/trivy-db/pkg/db"
|
||||
"github.com/aquasecurity/trivy-db/pkg/types"
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/commands/artifact"
|
||||
"github.com/aquasecurity/trivy/pkg/commands/client"
|
||||
"github.com/aquasecurity/trivy/pkg/commands/plugin"
|
||||
"github.com/aquasecurity/trivy/pkg/commands/server"
|
||||
tdb "github.com/aquasecurity/trivy/pkg/db"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/utils"
|
||||
"github.com/aquasecurity/trivy/pkg/vulnerability"
|
||||
)
|
||||
@@ -56,7 +57,7 @@ var (
|
||||
severityFlag = cli.StringFlag{
|
||||
Name: "severity",
|
||||
Aliases: []string{"s"},
|
||||
Value: strings.Join(types.SeverityNames, ","),
|
||||
Value: strings.Join(dbTypes.SeverityNames, ","),
|
||||
Usage: "severities of vulnerabilities to be displayed (comma separated)",
|
||||
EnvVars: []string{"TRIVY_SEVERITY"},
|
||||
}
|
||||
@@ -134,11 +135,19 @@ var (
|
||||
|
||||
vulnTypeFlag = cli.StringFlag{
|
||||
Name: "vuln-type",
|
||||
Value: "os,library",
|
||||
Value: strings.Join([]string{types.VulnTypeOS, types.VulnTypeLibrary}, ","),
|
||||
Usage: "comma-separated list of vulnerability types (os,library)",
|
||||
EnvVars: []string{"TRIVY_VULN_TYPE"},
|
||||
}
|
||||
|
||||
securityChecksFlag = cli.StringFlag{
|
||||
Name: "security-checks",
|
||||
Value: types.SecurityCheckVulnerability,
|
||||
Usage: "comma-separated list of what security issues to detect (vuln,config)",
|
||||
EnvVars: []string{"TRIVY_SECURITY_CHECKS"},
|
||||
Hidden: true,
|
||||
}
|
||||
|
||||
cacheDirFlag = cli.StringFlag{
|
||||
Name: "cache-dir",
|
||||
Value: utils.DefaultCacheDir(),
|
||||
@@ -231,6 +240,7 @@ var (
|
||||
&ignoreUnfixedFlag,
|
||||
&removedPkgsFlag,
|
||||
&vulnTypeFlag,
|
||||
&securityChecksFlag,
|
||||
&ignoreFileFlag,
|
||||
&timeoutFlag,
|
||||
&lightFlag,
|
||||
@@ -405,6 +415,7 @@ func NewFilesystemCommand() *cli.Command {
|
||||
&ignoreUnfixedFlag,
|
||||
&removedPkgsFlag,
|
||||
&vulnTypeFlag,
|
||||
&securityChecksFlag,
|
||||
&ignoreFileFlag,
|
||||
&cacheBackendFlag,
|
||||
&timeoutFlag,
|
||||
@@ -437,6 +448,7 @@ func NewRepositoryCommand() *cli.Command {
|
||||
&ignoreUnfixedFlag,
|
||||
&removedPkgsFlag,
|
||||
&vulnTypeFlag,
|
||||
&securityChecksFlag,
|
||||
&ignoreFileFlag,
|
||||
&cacheBackendFlag,
|
||||
&timeoutFlag,
|
||||
@@ -468,6 +480,7 @@ func NewClientCommand() *cli.Command {
|
||||
&ignoreUnfixedFlag,
|
||||
&removedPkgsFlag,
|
||||
&vulnTypeFlag,
|
||||
&securityChecksFlag,
|
||||
&ignoreFileFlag,
|
||||
&timeoutFlag,
|
||||
&ignorePolicy,
|
||||
@@ -519,9 +532,10 @@ func NewServerCommand() *cli.Command {
|
||||
// NewPluginCommand is the factory method to add plugin command
|
||||
func NewPluginCommand() *cli.Command {
|
||||
return &cli.Command{
|
||||
Name: "plugin",
|
||||
Aliases: []string{"p"},
|
||||
Usage: "manage plugins",
|
||||
Name: "plugin",
|
||||
Aliases: []string{"p"},
|
||||
ArgsUsage: "plugin_uri",
|
||||
Usage: "manage plugins",
|
||||
Subcommands: cli.Commands{
|
||||
{
|
||||
Name: "install",
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
package artifact
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/commands/config"
|
||||
)
|
||||
|
||||
// Config holds the artifact config
|
||||
type Config struct {
|
||||
config.GlobalConfig
|
||||
config.ArtifactConfig
|
||||
config.DBConfig
|
||||
config.ImageConfig
|
||||
config.ReportConfig
|
||||
config.CacheConfig
|
||||
|
||||
// deprecated
|
||||
onlyUpdate string
|
||||
// deprecated
|
||||
refresh bool
|
||||
// deprecated
|
||||
autoRefresh bool
|
||||
}
|
||||
|
||||
// NewConfig is the factory method to return config
|
||||
func NewConfig(c *cli.Context) (Config, error) {
|
||||
gc, err := config.NewGlobalConfig(c)
|
||||
if err != nil {
|
||||
return Config{}, xerrors.Errorf("failed to initialize global options: %w", err)
|
||||
}
|
||||
|
||||
return Config{
|
||||
GlobalConfig: gc,
|
||||
ArtifactConfig: config.NewArtifactConfig(c),
|
||||
DBConfig: config.NewDBConfig(c),
|
||||
ImageConfig: config.NewImageConfig(c),
|
||||
ReportConfig: config.NewReportConfig(c),
|
||||
CacheConfig: config.NewCacheConfig(c),
|
||||
|
||||
onlyUpdate: c.String("only-update"),
|
||||
refresh: c.Bool("refresh"),
|
||||
autoRefresh: c.Bool("auto-refresh"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Init initializes the artifact config
|
||||
func (c *Config) Init() error {
|
||||
if c.onlyUpdate != "" || c.refresh || c.autoRefresh {
|
||||
c.Logger.Warn("--only-update, --refresh and --auto-refresh are unnecessary and ignored now. These commands will be removed in the next version.")
|
||||
}
|
||||
|
||||
if err := c.initPreScanConfigs(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// --clear-cache, --download-db-only and --reset don't conduct the scan
|
||||
if c.skipScan() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := c.ArtifactConfig.Init(c.Context, c.Logger); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) initPreScanConfigs() error {
|
||||
if err := c.ReportConfig.Init(c.Logger); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.DBConfig.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.CacheConfig.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) skipScan() bool {
|
||||
if c.ClearCache || c.DownloadDBOnly || c.Reset {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -8,13 +8,14 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer"
|
||||
"github.com/aquasecurity/fanal/analyzer/config"
|
||||
"github.com/aquasecurity/fanal/cache"
|
||||
"github.com/aquasecurity/trivy/pkg/scanner"
|
||||
)
|
||||
|
||||
func filesystemScanner(ctx context.Context, dir string, ac cache.ArtifactCache, lac cache.LocalArtifactCache,
|
||||
_ time.Duration, disabled []analyzer.Type) (scanner.Scanner, func(), error) {
|
||||
s, cleanup, err := initializeFilesystemScanner(ctx, dir, ac, lac, disabled)
|
||||
_ time.Duration, disabled []analyzer.Type, opt config.ScannerOption) (scanner.Scanner, func(), error) {
|
||||
s, cleanup, err := initializeFilesystemScanner(ctx, dir, ac, lac, disabled, opt)
|
||||
if err != nil {
|
||||
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a filesystem scanner: %w", err)
|
||||
}
|
||||
@@ -23,15 +24,15 @@ func filesystemScanner(ctx context.Context, dir string, ac cache.ArtifactCache,
|
||||
|
||||
// FilesystemRun runs scan on filesystem
|
||||
func FilesystemRun(ctx *cli.Context) error {
|
||||
c, err := NewConfig(ctx)
|
||||
opt, err := NewOption(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
return xerrors.Errorf("option error: %w", err)
|
||||
}
|
||||
|
||||
// initialize config
|
||||
if err = c.Init(); err != nil {
|
||||
// initialize options
|
||||
if err = opt.Init(); err != nil {
|
||||
return xerrors.Errorf("failed to initialize options: %w", err)
|
||||
}
|
||||
|
||||
return run(ctx.Context, c, filesystemScanner)
|
||||
return Run(ctx.Context, opt, filesystemScanner, initFSCache)
|
||||
}
|
||||
|
||||
@@ -8,13 +8,14 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer"
|
||||
"github.com/aquasecurity/fanal/analyzer/config"
|
||||
"github.com/aquasecurity/fanal/cache"
|
||||
"github.com/aquasecurity/trivy/pkg/scanner"
|
||||
)
|
||||
|
||||
func archiveScanner(ctx context.Context, input string, ac cache.ArtifactCache, lac cache.LocalArtifactCache,
|
||||
timeout time.Duration, disabled []analyzer.Type) (scanner.Scanner, func(), error) {
|
||||
s, err := initializeArchiveScanner(ctx, input, ac, lac, timeout, disabled)
|
||||
timeout time.Duration, disabled []analyzer.Type, opt config.ScannerOption) (scanner.Scanner, func(), error) {
|
||||
s, err := initializeArchiveScanner(ctx, input, ac, lac, timeout, disabled, opt)
|
||||
if err != nil {
|
||||
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize the archive scanner: %w", err)
|
||||
}
|
||||
@@ -22,9 +23,8 @@ func archiveScanner(ctx context.Context, input string, ac cache.ArtifactCache, l
|
||||
}
|
||||
|
||||
func dockerScanner(ctx context.Context, imageName string, ac cache.ArtifactCache, lac cache.LocalArtifactCache,
|
||||
timeout time.Duration, disabled []analyzer.Type) (
|
||||
scanner.Scanner, func(), error) {
|
||||
s, cleanup, err := initializeDockerScanner(ctx, imageName, ac, lac, timeout, disabled)
|
||||
timeout time.Duration, disabled []analyzer.Type, opt config.ScannerOption) (scanner.Scanner, func(), error) {
|
||||
s, cleanup, err := initializeDockerScanner(ctx, imageName, ac, lac, timeout, disabled, opt)
|
||||
if err != nil {
|
||||
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a docker scanner: %w", err)
|
||||
}
|
||||
@@ -33,20 +33,20 @@ func dockerScanner(ctx context.Context, imageName string, ac cache.ArtifactCache
|
||||
|
||||
// ImageRun runs scan on docker image
|
||||
func ImageRun(ctx *cli.Context) error {
|
||||
c, err := NewConfig(ctx)
|
||||
opt, err := NewOption(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
return xerrors.Errorf("option error: %w", err)
|
||||
}
|
||||
|
||||
// initialize config
|
||||
if err = c.Init(); err != nil {
|
||||
return xerrors.Errorf("failed to initialize options: %w", err)
|
||||
// initialize options
|
||||
if err = opt.Init(); err != nil {
|
||||
return xerrors.Errorf("option initialize error: %w", err)
|
||||
}
|
||||
|
||||
if c.Input != "" {
|
||||
if opt.Input != "" {
|
||||
// scan tar file
|
||||
return run(ctx.Context, c, archiveScanner)
|
||||
return Run(ctx.Context, opt, archiveScanner, initFSCache)
|
||||
}
|
||||
|
||||
return run(ctx.Context, c, dockerScanner)
|
||||
return Run(ctx.Context, opt, dockerScanner, initFSCache)
|
||||
}
|
||||
|
||||
@@ -9,38 +9,41 @@ import (
|
||||
"github.com/google/wire"
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer"
|
||||
"github.com/aquasecurity/fanal/analyzer/config"
|
||||
"github.com/aquasecurity/fanal/cache"
|
||||
"github.com/aquasecurity/trivy/pkg/scanner"
|
||||
"github.com/aquasecurity/trivy/pkg/vulnerability"
|
||||
)
|
||||
|
||||
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache,
|
||||
localArtifactCache cache.LocalArtifactCache, timeout time.Duration, disableAnalyzers []analyzer.Type) (
|
||||
scanner.Scanner, func(), error) {
|
||||
localArtifactCache cache.LocalArtifactCache, timeout time.Duration, disableAnalyzers []analyzer.Type,
|
||||
configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
|
||||
wire.Build(scanner.StandaloneDockerSet)
|
||||
return scanner.Scanner{}, nil, nil
|
||||
}
|
||||
|
||||
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache,
|
||||
localArtifactCache cache.LocalArtifactCache, timeout time.Duration, disableAnalyzers []analyzer.Type) (
|
||||
scanner.Scanner, error) {
|
||||
localArtifactCache cache.LocalArtifactCache, timeout time.Duration, disableAnalyzers []analyzer.Type,
|
||||
configScannerOption config.ScannerOption) (scanner.Scanner, error) {
|
||||
wire.Build(scanner.StandaloneArchiveSet)
|
||||
return scanner.Scanner{}, nil
|
||||
}
|
||||
|
||||
func initializeFilesystemScanner(ctx context.Context, dir string, artifactCache cache.ArtifactCache,
|
||||
localArtifactCache cache.LocalArtifactCache, disableAnalyzers []analyzer.Type) (scanner.Scanner, func(), error) {
|
||||
localArtifactCache cache.LocalArtifactCache, disableAnalyzers []analyzer.Type, configScannerOption config.ScannerOption) (
|
||||
scanner.Scanner, func(), error) {
|
||||
wire.Build(scanner.StandaloneFilesystemSet)
|
||||
return scanner.Scanner{}, nil, nil
|
||||
}
|
||||
|
||||
func initializeRepositoryScanner(ctx context.Context, url string, artifactCache cache.ArtifactCache,
|
||||
localArtifactCache cache.LocalArtifactCache, disableAnalyzers []analyzer.Type) (scanner.Scanner, func(), error) {
|
||||
localArtifactCache cache.LocalArtifactCache, disableAnalyzers []analyzer.Type, configScannerOption config.ScannerOption) (
|
||||
scanner.Scanner, func(), error) {
|
||||
wire.Build(scanner.StandaloneRepositorySet)
|
||||
return scanner.Scanner{}, nil, nil
|
||||
}
|
||||
|
||||
func initializeVulnerabilityClient() vulnerability.Client {
|
||||
func initializeResultClient() vulnerability.Client {
|
||||
wire.Build(vulnerability.SuperSet)
|
||||
return vulnerability.Client{}
|
||||
}
|
||||
|
||||
88
pkg/commands/artifact/option.go
Normal file
88
pkg/commands/artifact/option.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package artifact
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/commands/option"
|
||||
)
|
||||
|
||||
// Option holds the artifact options
|
||||
type Option struct {
|
||||
option.GlobalOption
|
||||
option.ArtifactOption
|
||||
option.DBOption
|
||||
option.ImageOption
|
||||
option.ReportOption
|
||||
option.CacheOption
|
||||
|
||||
// deprecated
|
||||
onlyUpdate string
|
||||
// deprecated
|
||||
refresh bool
|
||||
// deprecated
|
||||
autoRefresh bool
|
||||
}
|
||||
|
||||
// NewOption is the factory method to return options
|
||||
func NewOption(c *cli.Context) (Option, error) {
|
||||
gc, err := option.NewGlobalOption(c)
|
||||
if err != nil {
|
||||
return Option{}, xerrors.Errorf("failed to initialize global options: %w", err)
|
||||
}
|
||||
|
||||
return Option{
|
||||
GlobalOption: gc,
|
||||
ArtifactOption: option.NewArtifactOption(c),
|
||||
DBOption: option.NewDBOption(c),
|
||||
ImageOption: option.NewImageOption(c),
|
||||
ReportOption: option.NewReportOption(c),
|
||||
CacheOption: option.NewCacheOption(c),
|
||||
|
||||
onlyUpdate: c.String("only-update"),
|
||||
refresh: c.Bool("refresh"),
|
||||
autoRefresh: c.Bool("auto-refresh"),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Init initializes the artifact options
|
||||
func (c *Option) Init() error {
|
||||
if c.onlyUpdate != "" || c.refresh || c.autoRefresh {
|
||||
c.Logger.Warn("--only-update, --refresh and --auto-refresh are unnecessary and ignored now. These commands will be removed in the next version.")
|
||||
}
|
||||
|
||||
if err := c.initPreScanOptions(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// --clear-cache, --download-db-only and --reset don't conduct the scan
|
||||
if c.skipScan() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := c.ArtifactOption.Init(c.Context, c.Logger); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Option) initPreScanOptions() error {
|
||||
if err := c.ReportOption.Init(c.Logger); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.DBOption.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.CacheOption.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Option) skipScan() bool {
|
||||
if c.ClearCache || c.DownloadDBOnly || c.Reset {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -12,53 +12,48 @@ import (
|
||||
"go.uber.org/zap/zaptest/observer"
|
||||
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/commands/config"
|
||||
"github.com/aquasecurity/trivy/pkg/commands/option"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
)
|
||||
|
||||
func TestConfig_Init(t *testing.T) {
|
||||
func TestOption_Init(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
globalConfig config.GlobalConfig
|
||||
dbConfig config.DBConfig
|
||||
imageConfig config.ImageConfig
|
||||
reportConfig config.ReportConfig
|
||||
args []string
|
||||
logs []string
|
||||
want Config
|
||||
wantErr string
|
||||
name string
|
||||
args []string
|
||||
logs []string
|
||||
want Option
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
reportConfig: config.ReportConfig{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
VulnType: []string{"os"},
|
||||
},
|
||||
args: []string{"--severity", "CRITICAL", "--vuln-type", "os", "--quiet", "alpine:3.10"},
|
||||
want: Config{
|
||||
GlobalConfig: config.GlobalConfig{
|
||||
want: Option{
|
||||
GlobalOption: option.GlobalOption{
|
||||
Quiet: true,
|
||||
},
|
||||
ArtifactConfig: config.ArtifactConfig{
|
||||
ArtifactOption: option.ArtifactOption{
|
||||
Target: "alpine:3.10",
|
||||
},
|
||||
ReportConfig: config.ReportConfig{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
VulnType: []string{"os"},
|
||||
Output: os.Stdout,
|
||||
ReportOption: option.ReportOption{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
VulnType: []string{types.VulnTypeOS},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
Output: os.Stdout,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path: reset",
|
||||
args: []string{"--reset"},
|
||||
want: Config{
|
||||
DBConfig: config.DBConfig{
|
||||
want: Option{
|
||||
DBOption: option.DBOption{
|
||||
Reset: true,
|
||||
},
|
||||
ReportConfig: config.ReportConfig{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{"os", "library"},
|
||||
ReportOption: option.ReportOption{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -68,13 +63,14 @@ func TestConfig_Init(t *testing.T) {
|
||||
logs: []string{
|
||||
"unknown severity option: unknown severity: INVALID",
|
||||
},
|
||||
want: Config{
|
||||
ReportConfig: config.ReportConfig{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityUnknown},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{"os", "library"},
|
||||
want: Option{
|
||||
ReportOption: option.ReportOption{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityUnknown},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
},
|
||||
ArtifactConfig: config.ArtifactConfig{
|
||||
ArtifactOption: option.ArtifactOption{
|
||||
Target: "centos:7",
|
||||
},
|
||||
},
|
||||
@@ -85,13 +81,14 @@ func TestConfig_Init(t *testing.T) {
|
||||
logs: []string{
|
||||
"--only-update, --refresh and --auto-refresh are unnecessary and ignored now. These commands will be removed in the next version.",
|
||||
},
|
||||
want: Config{
|
||||
ReportConfig: config.ReportConfig{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{"os", "library"},
|
||||
want: Option{
|
||||
ReportOption: option.ReportOption{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
},
|
||||
ArtifactConfig: config.ArtifactConfig{
|
||||
ArtifactOption: option.ArtifactOption{
|
||||
Target: "debian:buster",
|
||||
},
|
||||
onlyUpdate: "alpine",
|
||||
@@ -103,14 +100,15 @@ func TestConfig_Init(t *testing.T) {
|
||||
logs: []string{
|
||||
"--template is ignored because --format template is not specified. Use --template option with --format template option.",
|
||||
},
|
||||
want: Config{
|
||||
ReportConfig: config.ReportConfig{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{"os", "library"},
|
||||
Template: "@contrib/gitlab.tpl",
|
||||
want: Option{
|
||||
ReportOption: option.ReportOption{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
Template: "@contrib/gitlab.tpl",
|
||||
},
|
||||
ArtifactConfig: config.ArtifactConfig{
|
||||
ArtifactOption: option.ArtifactOption{
|
||||
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
|
||||
},
|
||||
},
|
||||
@@ -121,15 +119,16 @@ func TestConfig_Init(t *testing.T) {
|
||||
logs: []string{
|
||||
"--template is ignored because --format json is specified. Use --template option with --format template option.",
|
||||
},
|
||||
want: Config{
|
||||
ReportConfig: config.ReportConfig{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{"os", "library"},
|
||||
Template: "@contrib/gitlab.tpl",
|
||||
Format: "json",
|
||||
want: Option{
|
||||
ReportOption: option.ReportOption{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
Template: "@contrib/gitlab.tpl",
|
||||
Format: "json",
|
||||
},
|
||||
ArtifactConfig: config.ArtifactConfig{
|
||||
ArtifactOption: option.ArtifactOption{
|
||||
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
|
||||
},
|
||||
},
|
||||
@@ -140,14 +139,15 @@ func TestConfig_Init(t *testing.T) {
|
||||
logs: []string{
|
||||
"--format template is ignored because --template not is specified. Specify --template option when you use --format template.",
|
||||
},
|
||||
want: Config{
|
||||
ReportConfig: config.ReportConfig{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityMedium},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{"os", "library"},
|
||||
Format: "template",
|
||||
want: Option{
|
||||
ReportOption: option.ReportOption{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityMedium},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
Format: "template",
|
||||
},
|
||||
ArtifactConfig: config.ArtifactConfig{
|
||||
ArtifactOption: option.ArtifactOption{
|
||||
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
|
||||
},
|
||||
},
|
||||
@@ -181,6 +181,7 @@ func TestConfig_Init(t *testing.T) {
|
||||
set.Bool("auto-refresh", false, "")
|
||||
set.String("severity", "CRITICAL", "")
|
||||
set.String("vuln-type", "os,library", "")
|
||||
set.String("security-checks", "vuln", "")
|
||||
set.String("only-update", "", "")
|
||||
set.String("template", "", "")
|
||||
set.String("format", "", "")
|
||||
@@ -188,10 +189,10 @@ func TestConfig_Init(t *testing.T) {
|
||||
ctx := cli.NewContext(app, set, nil)
|
||||
_ = set.Parse(tt.args)
|
||||
|
||||
c, err := NewConfig(ctx)
|
||||
c, err := NewOption(ctx)
|
||||
require.NoError(t, err, err)
|
||||
|
||||
c.GlobalConfig.Logger = logger.Sugar()
|
||||
c.GlobalOption.Logger = logger.Sugar()
|
||||
err = c.Init()
|
||||
|
||||
// tests log messages
|
||||
@@ -211,8 +212,8 @@ func TestConfig_Init(t *testing.T) {
|
||||
assert.NoError(t, err, tt.name)
|
||||
}
|
||||
|
||||
tt.want.GlobalConfig.Context = ctx
|
||||
tt.want.GlobalConfig.Logger = logger.Sugar()
|
||||
tt.want.GlobalOption.Context = ctx
|
||||
tt.want.GlobalOption.Logger = logger.Sugar()
|
||||
assert.Equal(t, tt.want, c, tt.name)
|
||||
})
|
||||
}
|
||||
@@ -8,14 +8,14 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer"
|
||||
"github.com/aquasecurity/fanal/analyzer/config"
|
||||
"github.com/aquasecurity/fanal/cache"
|
||||
"github.com/aquasecurity/trivy/pkg/scanner"
|
||||
)
|
||||
|
||||
func repositoryScanner(ctx context.Context, dir string, ac cache.ArtifactCache, lac cache.LocalArtifactCache,
|
||||
_ time.Duration, disabled []analyzer.Type) (
|
||||
scanner.Scanner, func(), error) {
|
||||
s, cleanup, err := initializeRepositoryScanner(ctx, dir, ac, lac, disabled)
|
||||
_ time.Duration, disabled []analyzer.Type, opt config.ScannerOption) (scanner.Scanner, func(), error) {
|
||||
s, cleanup, err := initializeRepositoryScanner(ctx, dir, ac, lac, disabled, opt)
|
||||
if err != nil {
|
||||
return scanner.Scanner{}, func() {}, xerrors.Errorf("unable to initialize a filesystem scanner: %w", err)
|
||||
}
|
||||
@@ -24,15 +24,15 @@ func repositoryScanner(ctx context.Context, dir string, ac cache.ArtifactCache,
|
||||
|
||||
// RepositoryRun runs scan on repository
|
||||
func RepositoryRun(ctx *cli.Context) error {
|
||||
c, err := NewConfig(ctx)
|
||||
opt, err := NewOption(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
return xerrors.Errorf("option error: %w", err)
|
||||
}
|
||||
|
||||
// initialize config
|
||||
if err = c.Init(); err != nil {
|
||||
// initialize options
|
||||
if err = opt.Init(); err != nil {
|
||||
return xerrors.Errorf("failed to initialize options: %w", err)
|
||||
}
|
||||
|
||||
return run(ctx.Context, c, repositoryScanner)
|
||||
return Run(ctx.Context, opt, repositoryScanner, initFSCache)
|
||||
}
|
||||
|
||||
@@ -3,13 +3,13 @@ package artifact
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
l "log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer"
|
||||
"github.com/aquasecurity/fanal/analyzer/config"
|
||||
"github.com/aquasecurity/fanal/cache"
|
||||
"github.com/aquasecurity/trivy-db/pkg/db"
|
||||
"github.com/aquasecurity/trivy/pkg/commands/operation"
|
||||
@@ -24,25 +24,25 @@ var errSkipScan = errors.New("skip subsequent processes")
|
||||
|
||||
// InitializeScanner type to define initialize function signature
|
||||
type InitializeScanner func(context.Context, string, cache.ArtifactCache, cache.LocalArtifactCache, time.Duration,
|
||||
[]analyzer.Type) (scanner.Scanner, func(), error)
|
||||
[]analyzer.Type, config.ScannerOption) (scanner.Scanner, func(), error)
|
||||
|
||||
func run(ctx context.Context, conf Config, initializeScanner InitializeScanner) error {
|
||||
ctx, cancel := context.WithTimeout(ctx, conf.Timeout)
|
||||
// InitCache defines cache initializer
|
||||
type InitCache func(c Option) (cache.Cache, error)
|
||||
|
||||
// Run performs artifact scanning
|
||||
func Run(ctx context.Context, opt Option, initializeScanner InitializeScanner, initCache InitCache) error {
|
||||
ctx, cancel := context.WithTimeout(ctx, opt.Timeout)
|
||||
defer cancel()
|
||||
|
||||
err := runWithTimeout(ctx, conf, initializeScanner)
|
||||
if xerrors.Is(err, context.DeadlineExceeded) {
|
||||
log.Logger.Warn("Increase --timeout value")
|
||||
}
|
||||
return err
|
||||
return runWithTimeout(ctx, opt, initializeScanner, initCache)
|
||||
}
|
||||
|
||||
func runWithTimeout(ctx context.Context, conf Config, initializeScanner InitializeScanner) error {
|
||||
if err := log.InitLogger(conf.Debug, conf.Quiet); err != nil {
|
||||
l.Fatal(err)
|
||||
func runWithTimeout(ctx context.Context, opt Option, initializeScanner InitializeScanner, initCache InitCache) error {
|
||||
if err := log.InitLogger(opt.Debug, opt.Quiet); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cacheClient, err := initCache(conf)
|
||||
cacheClient, err := initCache(opt)
|
||||
if err != nil {
|
||||
if errors.Is(err, errSkipScan) {
|
||||
return nil
|
||||
@@ -51,34 +51,37 @@ func runWithTimeout(ctx context.Context, conf Config, initializeScanner Initiali
|
||||
}
|
||||
defer cacheClient.Close()
|
||||
|
||||
if err = initDB(conf); err != nil {
|
||||
if errors.Is(err, errSkipScan) {
|
||||
return nil
|
||||
// When scanning config files, it doesn't need to download the vulnerability database.
|
||||
if utils.StringInSlice(types.SecurityCheckVulnerability, opt.SecurityChecks) {
|
||||
if err = initDB(opt); err != nil {
|
||||
if errors.Is(err, errSkipScan) {
|
||||
return nil
|
||||
}
|
||||
return xerrors.Errorf("DB error: %w", err)
|
||||
}
|
||||
return xerrors.Errorf("DB error: %w", err)
|
||||
defer db.Close()
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
results, err := scan(ctx, conf, initializeScanner, cacheClient)
|
||||
results, err := scan(ctx, opt, initializeScanner, cacheClient)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("scan error: %w", err)
|
||||
}
|
||||
|
||||
results, err = filter(ctx, conf, results)
|
||||
results, err = filter(ctx, opt, results)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("filter error: %w", err)
|
||||
}
|
||||
|
||||
if err = report.WriteResults(conf.Format, conf.Output, conf.Severities, results, conf.Template, conf.Light); err != nil {
|
||||
if err = report.WriteResults(opt.Format, opt.Output, opt.Severities, results, opt.Template, opt.Light); err != nil {
|
||||
return xerrors.Errorf("unable to write results: %w", err)
|
||||
}
|
||||
|
||||
exit(conf, results)
|
||||
exit(opt, results)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initCache(c Config) (operation.Cache, error) {
|
||||
func initFSCache(c Option) (cache.Cache, error) {
|
||||
utils.SetCacheDir(c.CacheDir)
|
||||
cache, err := operation.NewCache(c.CacheBackend)
|
||||
if err != nil {
|
||||
@@ -95,7 +98,7 @@ func initCache(c Config) (operation.Cache, error) {
|
||||
}
|
||||
if c.ClearCache {
|
||||
defer cache.Close()
|
||||
if err = cache.ClearImages(); err != nil {
|
||||
if err = cache.ClearArtifacts(); err != nil {
|
||||
return operation.Cache{}, xerrors.Errorf("cache clear error: %w", err)
|
||||
}
|
||||
return operation.Cache{}, errSkipScan
|
||||
@@ -103,7 +106,7 @@ func initCache(c Config) (operation.Cache, error) {
|
||||
return cache, nil
|
||||
}
|
||||
|
||||
func initDB(c Config) error {
|
||||
func initDB(c Option) error {
|
||||
// download the database file
|
||||
noProgress := c.Quiet || c.NoProgress
|
||||
if err := operation.DownloadDB(c.AppVersion, c.CacheDir, noProgress, c.Light, c.SkipUpdate); err != nil {
|
||||
@@ -120,29 +123,36 @@ func initDB(c Config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func scan(ctx context.Context, conf Config, initializeScanner InitializeScanner, cacheClient cache.Cache) (
|
||||
func scan(ctx context.Context, opt Option, initializeScanner InitializeScanner, cacheClient cache.Cache) (
|
||||
report.Results, error) {
|
||||
target := conf.Target
|
||||
if conf.Input != "" {
|
||||
target = conf.Input
|
||||
target := opt.Target
|
||||
if opt.Input != "" {
|
||||
target = opt.Input
|
||||
}
|
||||
|
||||
scanOptions := types.ScanOptions{
|
||||
VulnType: conf.VulnType,
|
||||
ScanRemovedPackages: conf.ScanRemovedPkgs, // this is valid only for image subcommand
|
||||
ListAllPackages: conf.ListAllPkgs,
|
||||
SkipFiles: conf.SkipFiles,
|
||||
SkipDirs: conf.SkipDirs,
|
||||
VulnType: opt.VulnType,
|
||||
SecurityChecks: opt.SecurityChecks,
|
||||
ScanRemovedPackages: opt.ScanRemovedPkgs, // this is valid only for image subcommand
|
||||
ListAllPackages: opt.ListAllPkgs,
|
||||
SkipFiles: opt.SkipFiles,
|
||||
SkipDirs: opt.SkipDirs,
|
||||
}
|
||||
log.Logger.Debugf("Vulnerability type: %s", scanOptions.VulnType)
|
||||
|
||||
// It doesn't analyze apk commands by default.
|
||||
disabledAnalyzers := []analyzer.Type{analyzer.TypeApkCommand}
|
||||
if conf.ScanRemovedPkgs {
|
||||
if opt.ScanRemovedPkgs {
|
||||
disabledAnalyzers = []analyzer.Type{}
|
||||
}
|
||||
|
||||
s, cleanup, err := initializeScanner(ctx, target, cacheClient, cacheClient, conf.Timeout, disabledAnalyzers)
|
||||
// TODO: fix the scanner option and enable config analyzers once we finalize the specification of config scanning.
|
||||
configScannerOptions := config.ScannerOption{}
|
||||
disabledAnalyzers = append(disabledAnalyzers, analyzer.TypeYaml, analyzer.TypeTOML, analyzer.TypeJSON,
|
||||
analyzer.TypeDockerfile, analyzer.TypeHCL)
|
||||
|
||||
s, cleanup, err := initializeScanner(ctx, target, cacheClient, cacheClient, opt.Timeout,
|
||||
disabledAnalyzers, configScannerOptions)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("unable to initialize a scanner: %w", err)
|
||||
}
|
||||
@@ -155,12 +165,12 @@ func scan(ctx context.Context, conf Config, initializeScanner InitializeScanner,
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func filter(ctx context.Context, conf Config, results report.Results) (report.Results, error) {
|
||||
vulnClient := initializeVulnerabilityClient()
|
||||
func filter(ctx context.Context, opt Option, results report.Results) (report.Results, error) {
|
||||
resultClient := initializeResultClient()
|
||||
for i := range results {
|
||||
vulnClient.FillInfo(results[i].Vulnerabilities, results[i].Type)
|
||||
vulns, err := vulnClient.Filter(ctx, results[i].Vulnerabilities,
|
||||
conf.Severities, conf.IgnoreUnfixed, conf.IgnoreFile, conf.IgnorePolicy)
|
||||
resultClient.FillInfo(results[i].Vulnerabilities, results[i].Type)
|
||||
vulns, err := resultClient.Filter(ctx, results[i].Vulnerabilities,
|
||||
opt.Severities, opt.IgnoreUnfixed, opt.IgnoreFile, opt.IgnorePolicy)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("unable to filter vulnerabilities: %w", err)
|
||||
}
|
||||
@@ -169,12 +179,8 @@ func filter(ctx context.Context, conf Config, results report.Results) (report.Re
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func exit(c Config, results report.Results) {
|
||||
if c.ExitCode != 0 {
|
||||
for _, result := range results {
|
||||
if len(result.Vulnerabilities) > 0 {
|
||||
os.Exit(c.ExitCode)
|
||||
}
|
||||
}
|
||||
func exit(c Option, results report.Results) {
|
||||
if c.ExitCode != 0 && results.Failed() {
|
||||
os.Exit(c.ExitCode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ package artifact
|
||||
import (
|
||||
"context"
|
||||
"github.com/aquasecurity/fanal/analyzer"
|
||||
"github.com/aquasecurity/fanal/analyzer/config"
|
||||
"github.com/aquasecurity/fanal/applier"
|
||||
image2 "github.com/aquasecurity/fanal/artifact/image"
|
||||
local2 "github.com/aquasecurity/fanal/artifact/local"
|
||||
@@ -25,7 +26,7 @@ import (
|
||||
|
||||
// Injectors from inject.go:
|
||||
|
||||
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, timeout time.Duration, disableAnalyzers []analyzer.Type) (scanner.Scanner, func(), error) {
|
||||
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, timeout time.Duration, disableAnalyzers []analyzer.Type, configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
|
||||
applierApplier := applier.NewApplier(localArtifactCache)
|
||||
detector := ospkg.Detector{}
|
||||
localScanner := local.NewScanner(applierApplier, detector)
|
||||
@@ -37,14 +38,18 @@ func initializeDockerScanner(ctx context.Context, imageName string, artifactCach
|
||||
if err != nil {
|
||||
return scanner.Scanner{}, nil, err
|
||||
}
|
||||
artifact := image2.NewArtifact(imageImage, artifactCache, disableAnalyzers)
|
||||
artifact, err := image2.NewArtifact(imageImage, artifactCache, disableAnalyzers, configScannerOption)
|
||||
if err != nil {
|
||||
cleanup()
|
||||
return scanner.Scanner{}, nil, err
|
||||
}
|
||||
scannerScanner := scanner.NewScanner(localScanner, artifact)
|
||||
return scannerScanner, func() {
|
||||
cleanup()
|
||||
}, nil
|
||||
}
|
||||
|
||||
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, timeout time.Duration, disableAnalyzers []analyzer.Type) (scanner.Scanner, error) {
|
||||
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, timeout time.Duration, disableAnalyzers []analyzer.Type, configScannerOption config.ScannerOption) (scanner.Scanner, error) {
|
||||
applierApplier := applier.NewApplier(localArtifactCache)
|
||||
detector := ospkg.Detector{}
|
||||
localScanner := local.NewScanner(applierApplier, detector)
|
||||
@@ -52,26 +57,32 @@ func initializeArchiveScanner(ctx context.Context, filePath string, artifactCach
|
||||
if err != nil {
|
||||
return scanner.Scanner{}, err
|
||||
}
|
||||
artifact := image2.NewArtifact(imageImage, artifactCache, disableAnalyzers)
|
||||
artifact, err := image2.NewArtifact(imageImage, artifactCache, disableAnalyzers, configScannerOption)
|
||||
if err != nil {
|
||||
return scanner.Scanner{}, err
|
||||
}
|
||||
scannerScanner := scanner.NewScanner(localScanner, artifact)
|
||||
return scannerScanner, nil
|
||||
}
|
||||
|
||||
func initializeFilesystemScanner(ctx context.Context, dir string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, disableAnalyzers []analyzer.Type) (scanner.Scanner, func(), error) {
|
||||
func initializeFilesystemScanner(ctx context.Context, dir string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, disableAnalyzers []analyzer.Type, configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
|
||||
applierApplier := applier.NewApplier(localArtifactCache)
|
||||
detector := ospkg.Detector{}
|
||||
localScanner := local.NewScanner(applierApplier, detector)
|
||||
artifact := local2.NewArtifact(dir, artifactCache, disableAnalyzers)
|
||||
artifact, err := local2.NewArtifact(dir, artifactCache, disableAnalyzers, configScannerOption)
|
||||
if err != nil {
|
||||
return scanner.Scanner{}, nil, err
|
||||
}
|
||||
scannerScanner := scanner.NewScanner(localScanner, artifact)
|
||||
return scannerScanner, func() {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func initializeRepositoryScanner(ctx context.Context, url string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, disableAnalyzers []analyzer.Type) (scanner.Scanner, func(), error) {
|
||||
func initializeRepositoryScanner(ctx context.Context, url string, artifactCache cache.ArtifactCache, localArtifactCache cache.LocalArtifactCache, disableAnalyzers []analyzer.Type, configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
|
||||
applierApplier := applier.NewApplier(localArtifactCache)
|
||||
detector := ospkg.Detector{}
|
||||
localScanner := local.NewScanner(applierApplier, detector)
|
||||
artifact, cleanup, err := remote.NewArtifact(url, artifactCache, disableAnalyzers)
|
||||
artifact, cleanup, err := remote.NewArtifact(url, artifactCache, disableAnalyzers, configScannerOption)
|
||||
if err != nil {
|
||||
return scanner.Scanner{}, nil, err
|
||||
}
|
||||
@@ -81,8 +92,8 @@ func initializeRepositoryScanner(ctx context.Context, url string, artifactCache
|
||||
}, nil
|
||||
}
|
||||
|
||||
func initializeVulnerabilityClient() vulnerability.Client {
|
||||
config := db.Config{}
|
||||
client := vulnerability.NewClient(config)
|
||||
func initializeResultClient() vulnerability.Client {
|
||||
dbConfig := db.Config{}
|
||||
client := vulnerability.NewClient(dbConfig)
|
||||
return client
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/google/wire"
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer"
|
||||
"github.com/aquasecurity/fanal/analyzer/config"
|
||||
"github.com/aquasecurity/fanal/cache"
|
||||
"github.com/aquasecurity/trivy/pkg/rpc/client"
|
||||
"github.com/aquasecurity/trivy/pkg/scanner"
|
||||
@@ -16,18 +17,20 @@ import (
|
||||
)
|
||||
|
||||
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders,
|
||||
url client.RemoteURL, timeout time.Duration, disabled []analyzer.Type) (scanner.Scanner, func(), error) {
|
||||
url client.RemoteURL, timeout time.Duration, disabled []analyzer.Type, configScannerOption config.ScannerOption) (
|
||||
scanner.Scanner, func(), error) {
|
||||
wire.Build(scanner.RemoteDockerSet)
|
||||
return scanner.Scanner{}, nil, nil
|
||||
}
|
||||
|
||||
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders,
|
||||
url client.RemoteURL, timeout time.Duration, disabled []analyzer.Type) (scanner.Scanner, error) {
|
||||
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache,
|
||||
customHeaders client.CustomHeaders, url client.RemoteURL, timeout time.Duration, disabled []analyzer.Type,
|
||||
configScannerOption config.ScannerOption) (scanner.Scanner, error) {
|
||||
wire.Build(scanner.RemoteArchiveSet)
|
||||
return scanner.Scanner{}, nil
|
||||
}
|
||||
|
||||
func initializeVulnerabilityClient() vulnerability.Client {
|
||||
func initializeResultClient() vulnerability.Client {
|
||||
wire.Build(vulnerability.SuperSet)
|
||||
return vulnerability.Client{}
|
||||
}
|
||||
|
||||
@@ -7,15 +7,15 @@ import (
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/commands/config"
|
||||
"github.com/aquasecurity/trivy/pkg/commands/option"
|
||||
)
|
||||
|
||||
// Config holds the Trivy client config
|
||||
type Config struct {
|
||||
config.GlobalConfig
|
||||
config.ArtifactConfig
|
||||
config.ImageConfig
|
||||
config.ReportConfig
|
||||
// Option holds the Trivy client options
|
||||
type Option struct {
|
||||
option.GlobalOption
|
||||
option.ArtifactOption
|
||||
option.ImageOption
|
||||
option.ReportOption
|
||||
|
||||
RemoteAddr string
|
||||
token string
|
||||
@@ -26,18 +26,18 @@ type Config struct {
|
||||
CustomHeaders http.Header
|
||||
}
|
||||
|
||||
// NewConfig is the factory method for Config
|
||||
func NewConfig(c *cli.Context) (Config, error) {
|
||||
gc, err := config.NewGlobalConfig(c)
|
||||
// NewOption is the factory method for Option
|
||||
func NewOption(c *cli.Context) (Option, error) {
|
||||
gc, err := option.NewGlobalOption(c)
|
||||
if err != nil {
|
||||
return Config{}, xerrors.Errorf("failed to initialize global options: %w", err)
|
||||
return Option{}, xerrors.Errorf("failed to initialize global options: %w", err)
|
||||
}
|
||||
|
||||
return Config{
|
||||
GlobalConfig: gc,
|
||||
ArtifactConfig: config.NewArtifactConfig(c),
|
||||
ImageConfig: config.NewImageConfig(c),
|
||||
ReportConfig: config.NewReportConfig(c),
|
||||
return Option{
|
||||
GlobalOption: gc,
|
||||
ArtifactOption: option.NewArtifactOption(c),
|
||||
ImageOption: option.NewImageOption(c),
|
||||
ReportOption: option.NewReportOption(c),
|
||||
RemoteAddr: c.String("remote"),
|
||||
token: c.String("token"),
|
||||
tokenHeader: c.String("token-header"),
|
||||
@@ -45,8 +45,8 @@ func NewConfig(c *cli.Context) (Config, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Init initializes the config
|
||||
func (c *Config) Init() (err error) {
|
||||
// Init initializes the options
|
||||
func (c *Option) Init() (err error) {
|
||||
// --clear-cache doesn't conduct the scan
|
||||
if c.ClearCache {
|
||||
return nil
|
||||
@@ -59,11 +59,11 @@ func (c *Config) Init() (err error) {
|
||||
c.CustomHeaders.Set(c.tokenHeader, c.token)
|
||||
}
|
||||
|
||||
if err := c.ReportConfig.Init(c.Logger); err != nil {
|
||||
if err := c.ReportOption.Init(c.Logger); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.ArtifactConfig.Init(c.Context, c.Logger); err != nil {
|
||||
if err := c.ArtifactOption.Init(c.Context, c.Logger); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"flag"
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -14,38 +13,33 @@ import (
|
||||
"go.uber.org/zap/zaptest/observer"
|
||||
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/commands/config"
|
||||
"github.com/aquasecurity/trivy/pkg/commands/option"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
)
|
||||
|
||||
func TestConfig_Init(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
globalConfig config.GlobalConfig
|
||||
imageConfig config.ImageConfig
|
||||
reportConfig config.ReportConfig
|
||||
args []string
|
||||
logs []string
|
||||
want Config
|
||||
wantErr string
|
||||
name string
|
||||
args []string
|
||||
logs []string
|
||||
want Option
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
reportConfig: config.ReportConfig{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
VulnType: []string{"os"},
|
||||
},
|
||||
args: []string{"--severity", "CRITICAL", "--vuln-type", "os", "--quiet", "alpine:3.10"},
|
||||
want: Config{
|
||||
GlobalConfig: config.GlobalConfig{
|
||||
want: Option{
|
||||
GlobalOption: option.GlobalOption{
|
||||
Quiet: true,
|
||||
},
|
||||
ArtifactConfig: config.ArtifactConfig{
|
||||
ArtifactOption: option.ArtifactOption{
|
||||
Target: "alpine:3.10",
|
||||
},
|
||||
ReportConfig: config.ReportConfig{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
VulnType: []string{"os"},
|
||||
Output: os.Stdout,
|
||||
ReportOption: option.ReportOption{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
VulnType: []string{types.VulnTypeOS},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
Output: os.Stdout,
|
||||
},
|
||||
CustomHeaders: http.Header{},
|
||||
},
|
||||
@@ -53,13 +47,14 @@ func TestConfig_Init(t *testing.T) {
|
||||
{
|
||||
name: "happy path with token and token header",
|
||||
args: []string{"--token", "secret", "--token-header", "X-Trivy-Token", "alpine:3.11"},
|
||||
want: Config{
|
||||
ReportConfig: config.ReportConfig{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{"os", "library"},
|
||||
want: Option{
|
||||
ReportOption: option.ReportOption{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
},
|
||||
ArtifactConfig: config.ArtifactConfig{
|
||||
ArtifactOption: option.ArtifactOption{
|
||||
Target: "alpine:3.11",
|
||||
},
|
||||
token: "secret",
|
||||
@@ -72,13 +67,14 @@ func TestConfig_Init(t *testing.T) {
|
||||
{
|
||||
name: "happy path with good custom headers",
|
||||
args: []string{"--custom-headers", "foo:bar", "alpine:3.11"},
|
||||
want: Config{
|
||||
ReportConfig: config.ReportConfig{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{"os", "library"},
|
||||
want: Option{
|
||||
ReportOption: option.ReportOption{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
},
|
||||
ArtifactConfig: config.ArtifactConfig{
|
||||
ArtifactOption: option.ArtifactOption{
|
||||
Target: "alpine:3.11",
|
||||
},
|
||||
customHeaders: []string{"foo:bar"},
|
||||
@@ -90,13 +86,14 @@ func TestConfig_Init(t *testing.T) {
|
||||
{
|
||||
name: "happy path with bad custom headers",
|
||||
args: []string{"--custom-headers", "foobaz", "alpine:3.11"},
|
||||
want: Config{
|
||||
ReportConfig: config.ReportConfig{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{"os", "library"},
|
||||
want: Option{
|
||||
ReportOption: option.ReportOption{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
},
|
||||
ArtifactConfig: config.ArtifactConfig{
|
||||
ArtifactOption: option.ArtifactOption{
|
||||
Target: "alpine:3.11",
|
||||
},
|
||||
customHeaders: []string{"foobaz"},
|
||||
@@ -109,13 +106,14 @@ func TestConfig_Init(t *testing.T) {
|
||||
logs: []string{
|
||||
"unknown severity option: unknown severity: INVALID",
|
||||
},
|
||||
want: Config{
|
||||
ReportConfig: config.ReportConfig{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityUnknown},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{"os", "library"},
|
||||
want: Option{
|
||||
ReportOption: option.ReportOption{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityUnknown},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
},
|
||||
ArtifactConfig: config.ArtifactConfig{
|
||||
ArtifactOption: option.ArtifactOption{
|
||||
Target: "centos:7",
|
||||
},
|
||||
CustomHeaders: http.Header{},
|
||||
@@ -127,14 +125,15 @@ func TestConfig_Init(t *testing.T) {
|
||||
logs: []string{
|
||||
"--template is ignored because --format template is not specified. Use --template option with --format template option.",
|
||||
},
|
||||
want: Config{
|
||||
ReportConfig: config.ReportConfig{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{"os", "library"},
|
||||
Template: "@contrib/gitlab.tpl",
|
||||
want: Option{
|
||||
ReportOption: option.ReportOption{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
Template: "@contrib/gitlab.tpl",
|
||||
},
|
||||
ArtifactConfig: config.ArtifactConfig{
|
||||
ArtifactOption: option.ArtifactOption{
|
||||
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
|
||||
},
|
||||
CustomHeaders: http.Header{},
|
||||
@@ -146,15 +145,16 @@ func TestConfig_Init(t *testing.T) {
|
||||
logs: []string{
|
||||
"--template is ignored because --format json is specified. Use --template option with --format template option.",
|
||||
},
|
||||
want: Config{
|
||||
ReportConfig: config.ReportConfig{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{"os", "library"},
|
||||
Template: "@contrib/gitlab.tpl",
|
||||
Format: "json",
|
||||
want: Option{
|
||||
ReportOption: option.ReportOption{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
Template: "@contrib/gitlab.tpl",
|
||||
Format: "json",
|
||||
},
|
||||
ArtifactConfig: config.ArtifactConfig{
|
||||
ArtifactOption: option.ArtifactOption{
|
||||
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
|
||||
},
|
||||
CustomHeaders: http.Header{},
|
||||
@@ -166,14 +166,15 @@ func TestConfig_Init(t *testing.T) {
|
||||
logs: []string{
|
||||
"--format template is ignored because --template not is specified. Specify --template option when you use --format template.",
|
||||
},
|
||||
want: Config{
|
||||
ReportConfig: config.ReportConfig{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityMedium},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{"os", "library"},
|
||||
Format: "template",
|
||||
want: Option{
|
||||
ReportOption: option.ReportOption{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityMedium},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
Format: "template",
|
||||
},
|
||||
ArtifactConfig: config.ArtifactConfig{
|
||||
ArtifactOption: option.ArtifactOption{
|
||||
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
|
||||
},
|
||||
CustomHeaders: http.Header{},
|
||||
@@ -185,14 +186,15 @@ func TestConfig_Init(t *testing.T) {
|
||||
logs: []string{
|
||||
"--format template is ignored because --template not is specified. Specify --template option when you use --format template.",
|
||||
},
|
||||
want: Config{
|
||||
ReportConfig: config.ReportConfig{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityMedium},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{"os", "library"},
|
||||
Format: "template",
|
||||
want: Option{
|
||||
ReportOption: option.ReportOption{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityMedium},
|
||||
Output: os.Stdout,
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
Format: "template",
|
||||
},
|
||||
ArtifactConfig: config.ArtifactConfig{
|
||||
ArtifactOption: option.ArtifactOption{
|
||||
Target: "gitlab/gitlab-ce:12.7.2-ce.0",
|
||||
},
|
||||
CustomHeaders: http.Header{},
|
||||
@@ -219,6 +221,7 @@ func TestConfig_Init(t *testing.T) {
|
||||
set.Bool("clear-cache", false, "")
|
||||
set.String("severity", "CRITICAL", "")
|
||||
set.String("vuln-type", "os,library", "")
|
||||
set.String("security-checks", "vuln", "")
|
||||
set.String("template", "", "")
|
||||
set.String("format", "", "")
|
||||
set.String("token", "", "")
|
||||
@@ -228,10 +231,10 @@ func TestConfig_Init(t *testing.T) {
|
||||
ctx := cli.NewContext(app, set, nil)
|
||||
_ = set.Parse(tt.args)
|
||||
|
||||
c, err := NewConfig(ctx)
|
||||
c, err := NewOption(ctx)
|
||||
require.NoError(t, err, err)
|
||||
|
||||
c.GlobalConfig.Logger = logger.Sugar()
|
||||
c.GlobalOption.Logger = logger.Sugar()
|
||||
err = c.Init()
|
||||
|
||||
// tests log messages
|
||||
@@ -251,8 +254,8 @@ func TestConfig_Init(t *testing.T) {
|
||||
assert.NoError(t, err, tt.name)
|
||||
}
|
||||
|
||||
tt.want.GlobalConfig.Context = ctx
|
||||
tt.want.GlobalConfig.Logger = logger.Sugar()
|
||||
tt.want.GlobalOption.Context = ctx
|
||||
tt.want.GlobalOption.Logger = logger.Sugar()
|
||||
assert.Equal(t, tt.want, c, tt.name)
|
||||
})
|
||||
}
|
||||
@@ -280,9 +283,8 @@ func Test_splitCustomHeaders(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := splitCustomHeaders(tt.args.headers); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("splitCustomHeaders() = %v, want %v", got, tt.want)
|
||||
}
|
||||
got := splitCustomHeaders(tt.args.headers)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer"
|
||||
"github.com/aquasecurity/fanal/analyzer/config"
|
||||
"github.com/aquasecurity/trivy/pkg/cache"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/report"
|
||||
@@ -18,44 +19,42 @@ import (
|
||||
)
|
||||
|
||||
// Run runs the scan
|
||||
func Run(ctx *cli.Context) error {
|
||||
c, err := NewConfig(ctx)
|
||||
func Run(cliCtx *cli.Context) error {
|
||||
opt, err := NewOption(cliCtx)
|
||||
if err != nil {
|
||||
return err
|
||||
return xerrors.Errorf("option error: %w", err)
|
||||
}
|
||||
return run(ctx.Context, c)
|
||||
}
|
||||
|
||||
func run(ctx context.Context, conf Config) error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), conf.Timeout)
|
||||
ctx, cancel := context.WithTimeout(cliCtx.Context, opt.Timeout)
|
||||
defer cancel()
|
||||
|
||||
err := runWithTimeout(ctx, conf)
|
||||
err = runWithTimeout(ctx, opt)
|
||||
if xerrors.Is(err, context.DeadlineExceeded) {
|
||||
log.Logger.Warn("Increase --timeout value")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func runWithTimeout(ctx context.Context, conf Config) error {
|
||||
if err := initialize(&conf); err != nil {
|
||||
func runWithTimeout(ctx context.Context, opt Option) error {
|
||||
if err := initialize(&opt); err != nil {
|
||||
return xerrors.Errorf("initialize error: %w", err)
|
||||
}
|
||||
|
||||
if conf.ClearCache {
|
||||
if opt.ClearCache {
|
||||
log.Logger.Warn("A client doesn't have image cache")
|
||||
return nil
|
||||
}
|
||||
|
||||
s, cleanup, err := initializeScanner(ctx, conf)
|
||||
s, cleanup, err := initializeScanner(ctx, opt)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("scanner initialize error: %w", err)
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
scanOptions := types.ScanOptions{
|
||||
VulnType: conf.VulnType,
|
||||
ScanRemovedPackages: conf.ScanRemovedPkgs,
|
||||
VulnType: opt.VulnType,
|
||||
SecurityChecks: opt.SecurityChecks,
|
||||
ScanRemovedPackages: opt.ScanRemovedPkgs,
|
||||
}
|
||||
log.Logger.Debugf("Vulnerability type: %s", scanOptions.VulnType)
|
||||
|
||||
@@ -64,56 +63,61 @@ func runWithTimeout(ctx context.Context, conf Config) error {
|
||||
return xerrors.Errorf("error in image scan: %w", err)
|
||||
}
|
||||
|
||||
vulnClient := initializeVulnerabilityClient()
|
||||
resultClient := initializeResultClient()
|
||||
for i := range results {
|
||||
vulns, err := vulnClient.Filter(ctx, results[i].Vulnerabilities,
|
||||
conf.Severities, conf.IgnoreUnfixed, conf.IgnoreFile, conf.IgnorePolicy)
|
||||
vulns, err := resultClient.Filter(ctx, results[i].Vulnerabilities,
|
||||
opt.Severities, opt.IgnoreUnfixed, opt.IgnoreFile, opt.IgnorePolicy)
|
||||
if err != nil {
|
||||
return err
|
||||
return xerrors.Errorf("filter error: %w", err)
|
||||
}
|
||||
results[i].Vulnerabilities = vulns
|
||||
}
|
||||
|
||||
if err = report.WriteResults(conf.Format, conf.Output, conf.Severities, results, conf.Template, false); err != nil {
|
||||
if err = report.WriteResults(opt.Format, opt.Output, opt.Severities, results, opt.Template, false); err != nil {
|
||||
return xerrors.Errorf("unable to write results: %w", err)
|
||||
}
|
||||
|
||||
exit(conf, results)
|
||||
exit(opt, results)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initialize(conf *Config) error {
|
||||
func initialize(opt *Option) error {
|
||||
// Initialize logger
|
||||
if err := log.InitLogger(conf.Debug, conf.Quiet); err != nil {
|
||||
if err := log.InitLogger(opt.Debug, opt.Quiet); err != nil {
|
||||
return xerrors.Errorf("failed to initialize a logger: %w", err)
|
||||
}
|
||||
|
||||
// Initialize config
|
||||
if err := conf.Init(); err != nil {
|
||||
// Initialize options
|
||||
if err := opt.Init(); err != nil {
|
||||
return xerrors.Errorf("failed to initialize options: %w", err)
|
||||
}
|
||||
|
||||
// configure cache dir
|
||||
utils.SetCacheDir(conf.CacheDir)
|
||||
utils.SetCacheDir(opt.CacheDir)
|
||||
log.Logger.Debugf("cache dir: %s", utils.CacheDir())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initializeScanner(ctx context.Context, conf Config) (scanner.Scanner, func(), error) {
|
||||
remoteCache := cache.NewRemoteCache(cache.RemoteURL(conf.RemoteAddr), conf.CustomHeaders)
|
||||
func initializeScanner(ctx context.Context, opt Option) (scanner.Scanner, func(), error) {
|
||||
remoteCache := cache.NewRemoteCache(cache.RemoteURL(opt.RemoteAddr), opt.CustomHeaders)
|
||||
|
||||
// By default, apk commands are not analyzed.
|
||||
disabledAnalyzers := []analyzer.Type{analyzer.TypeApkCommand}
|
||||
if conf.ScanRemovedPkgs {
|
||||
if opt.ScanRemovedPkgs {
|
||||
disabledAnalyzers = []analyzer.Type{}
|
||||
}
|
||||
|
||||
if conf.Input != "" {
|
||||
// TODO: fix the scanner option and enable config analyzers once we finalize the specification of config scanning.
|
||||
configScannerOptions := config.ScannerOption{}
|
||||
disabledAnalyzers = append(disabledAnalyzers, analyzer.TypeYaml, analyzer.TypeTOML, analyzer.TypeJSON,
|
||||
analyzer.TypeDockerfile, analyzer.TypeHCL)
|
||||
|
||||
if opt.Input != "" {
|
||||
// Scan tar file
|
||||
s, err := initializeArchiveScanner(ctx, conf.Input, remoteCache,
|
||||
client.CustomHeaders(conf.CustomHeaders), client.RemoteURL(conf.RemoteAddr), conf.Timeout, disabledAnalyzers)
|
||||
s, err := initializeArchiveScanner(ctx, opt.Input, remoteCache, client.CustomHeaders(opt.CustomHeaders),
|
||||
client.RemoteURL(opt.RemoteAddr), opt.Timeout, disabledAnalyzers, configScannerOptions)
|
||||
if err != nil {
|
||||
return scanner.Scanner{}, nil, xerrors.Errorf("unable to initialize the archive scanner: %w", err)
|
||||
}
|
||||
@@ -121,8 +125,8 @@ func initializeScanner(ctx context.Context, conf Config) (scanner.Scanner, func(
|
||||
}
|
||||
|
||||
// Scan an image in Docker Engine or Docker Registry
|
||||
s, cleanup, err := initializeDockerScanner(ctx, conf.Target, remoteCache,
|
||||
client.CustomHeaders(conf.CustomHeaders), client.RemoteURL(conf.RemoteAddr), conf.Timeout, disabledAnalyzers)
|
||||
s, cleanup, err := initializeDockerScanner(ctx, opt.Target, remoteCache, client.CustomHeaders(opt.CustomHeaders),
|
||||
client.RemoteURL(opt.RemoteAddr), opt.Timeout, disabledAnalyzers, configScannerOptions)
|
||||
if err != nil {
|
||||
return scanner.Scanner{}, nil, xerrors.Errorf("unable to initialize the docker scanner: %w", err)
|
||||
}
|
||||
@@ -130,7 +134,7 @@ func initializeScanner(ctx context.Context, conf Config) (scanner.Scanner, func(
|
||||
return s, cleanup, nil
|
||||
}
|
||||
|
||||
func exit(c Config, results report.Results) {
|
||||
func exit(c Option, results report.Results) {
|
||||
if c.ExitCode != 0 {
|
||||
for _, result := range results {
|
||||
if len(result.Vulnerabilities) > 0 {
|
||||
|
||||
@@ -8,6 +8,7 @@ package client
|
||||
import (
|
||||
"context"
|
||||
"github.com/aquasecurity/fanal/analyzer"
|
||||
"github.com/aquasecurity/fanal/analyzer/config"
|
||||
image2 "github.com/aquasecurity/fanal/artifact/image"
|
||||
"github.com/aquasecurity/fanal/cache"
|
||||
"github.com/aquasecurity/fanal/image"
|
||||
@@ -21,7 +22,7 @@ import (
|
||||
|
||||
// Injectors from inject.go:
|
||||
|
||||
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders, url client.RemoteURL, timeout time.Duration, disabled []analyzer.Type) (scanner.Scanner, func(), error) {
|
||||
func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders, url client.RemoteURL, timeout time.Duration, disabled []analyzer.Type, configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) {
|
||||
scannerScanner := client.NewProtobufClient(url)
|
||||
clientScanner := client.NewScanner(customHeaders, scannerScanner)
|
||||
dockerOption, err := types.GetDockerOption(timeout)
|
||||
@@ -32,27 +33,34 @@ func initializeDockerScanner(ctx context.Context, imageName string, artifactCach
|
||||
if err != nil {
|
||||
return scanner.Scanner{}, nil, err
|
||||
}
|
||||
artifact := image2.NewArtifact(imageImage, artifactCache, disabled)
|
||||
artifact, err := image2.NewArtifact(imageImage, artifactCache, disabled, configScannerOption)
|
||||
if err != nil {
|
||||
cleanup()
|
||||
return scanner.Scanner{}, nil, err
|
||||
}
|
||||
scanner2 := scanner.NewScanner(clientScanner, artifact)
|
||||
return scanner2, func() {
|
||||
cleanup()
|
||||
}, nil
|
||||
}
|
||||
|
||||
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders, url client.RemoteURL, timeout time.Duration, disabled []analyzer.Type) (scanner.Scanner, error) {
|
||||
func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders, url client.RemoteURL, timeout time.Duration, disabled []analyzer.Type, configScannerOption config.ScannerOption) (scanner.Scanner, error) {
|
||||
scannerScanner := client.NewProtobufClient(url)
|
||||
clientScanner := client.NewScanner(customHeaders, scannerScanner)
|
||||
imageImage, err := image.NewArchiveImage(filePath)
|
||||
if err != nil {
|
||||
return scanner.Scanner{}, err
|
||||
}
|
||||
artifact := image2.NewArtifact(imageImage, artifactCache, disabled)
|
||||
artifact, err := image2.NewArtifact(imageImage, artifactCache, disabled, configScannerOption)
|
||||
if err != nil {
|
||||
return scanner.Scanner{}, err
|
||||
}
|
||||
scanner2 := scanner.NewScanner(clientScanner, artifact)
|
||||
return scanner2, nil
|
||||
}
|
||||
|
||||
func initializeVulnerabilityClient() vulnerability.Client {
|
||||
config := db.Config{}
|
||||
vulnerabilityClient := vulnerability.NewClient(config)
|
||||
func initializeResultClient() vulnerability.Client {
|
||||
dbConfig := db.Config{}
|
||||
vulnerabilityClient := vulnerability.NewClient(dbConfig)
|
||||
return vulnerabilityClient
|
||||
}
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// ImageConfig holds the config for scanning images
|
||||
type ImageConfig struct {
|
||||
ScanRemovedPkgs bool
|
||||
ListAllPkgs bool
|
||||
}
|
||||
|
||||
// NewImageConfig is the factory method to return imageConfig
|
||||
func NewImageConfig(c *cli.Context) ImageConfig {
|
||||
return ImageConfig{
|
||||
ScanRemovedPkgs: c.Bool("removed-pkgs"),
|
||||
ListAllPkgs: c.Bool("list-all-pkgs"),
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
)
|
||||
|
||||
// ReportConfig holds the config for reporting scan results
|
||||
type ReportConfig struct {
|
||||
Format string
|
||||
Template string
|
||||
|
||||
IgnoreFile string
|
||||
IgnoreUnfixed bool
|
||||
ExitCode int
|
||||
IgnorePolicy string
|
||||
|
||||
// these variables are not exported
|
||||
vulnType string
|
||||
output string
|
||||
severities string
|
||||
|
||||
// these variables are populated by Init()
|
||||
VulnType []string
|
||||
Output *os.File
|
||||
Severities []dbTypes.Severity
|
||||
}
|
||||
|
||||
// NewReportConfig is the factory method to return ReportConfig
|
||||
func NewReportConfig(c *cli.Context) ReportConfig {
|
||||
return ReportConfig{
|
||||
output: c.String("output"),
|
||||
Format: c.String("format"),
|
||||
Template: c.String("template"),
|
||||
IgnorePolicy: c.String("ignore-policy"),
|
||||
|
||||
vulnType: c.String("vuln-type"),
|
||||
severities: c.String("severity"),
|
||||
IgnoreFile: c.String("ignorefile"),
|
||||
IgnoreUnfixed: c.Bool("ignore-unfixed"),
|
||||
ExitCode: c.Int("exit-code"),
|
||||
}
|
||||
}
|
||||
|
||||
// Init initializes the ReportConfig
|
||||
func (c *ReportConfig) Init(logger *zap.SugaredLogger) (err error) {
|
||||
if c.Template != "" {
|
||||
if c.Format == "" {
|
||||
logger.Warn("--template is ignored because --format template is not specified. Use --template option with --format template option.")
|
||||
} else if c.Format != "template" {
|
||||
logger.Warnf("--template is ignored because --format %s is specified. Use --template option with --format template option.", c.Format)
|
||||
}
|
||||
}
|
||||
if c.Format == "template" && c.Template == "" {
|
||||
logger.Warn("--format template is ignored because --template not is specified. Specify --template option when you use --format template.")
|
||||
}
|
||||
|
||||
c.Severities = c.splitSeverity(logger, c.severities)
|
||||
c.VulnType = strings.Split(c.vulnType, ",")
|
||||
|
||||
// for testability
|
||||
c.severities = ""
|
||||
c.vulnType = ""
|
||||
|
||||
c.Output = os.Stdout
|
||||
if c.output != "" {
|
||||
if c.Output, err = os.Create(c.output); err != nil {
|
||||
return xerrors.Errorf("failed to create an output file: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ReportConfig) splitSeverity(logger *zap.SugaredLogger, severity string) []dbTypes.Severity {
|
||||
logger.Debugf("Severities: %s", severity)
|
||||
var severities []dbTypes.Severity
|
||||
for _, s := range strings.Split(severity, ",") {
|
||||
severity, err := dbTypes.NewSeverity(s)
|
||||
if err != nil {
|
||||
logger.Warnf("unknown severity option: %s", err)
|
||||
}
|
||||
severities = append(severities, severity)
|
||||
}
|
||||
return severities
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zaptest/observer"
|
||||
)
|
||||
|
||||
func TestReportReportConfig_Init(t *testing.T) {
|
||||
type fields struct {
|
||||
output string
|
||||
Format string
|
||||
Template string
|
||||
vulnType string
|
||||
severities string
|
||||
IgnoreFile string
|
||||
IgnoreUnfixed bool
|
||||
ExitCode int
|
||||
VulnType []string
|
||||
Output *os.File
|
||||
Severities []dbTypes.Severity
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args []string
|
||||
logs []string
|
||||
want ReportConfig
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
fields: fields{
|
||||
severities: "CRITICAL",
|
||||
vulnType: "os",
|
||||
},
|
||||
args: []string{"alpine:3.10"},
|
||||
want: ReportConfig{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
VulnType: []string{"os"},
|
||||
Output: os.Stdout,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path with an unknown severity",
|
||||
fields: fields{
|
||||
severities: "CRITICAL,INVALID",
|
||||
vulnType: "os,library",
|
||||
},
|
||||
args: []string{"centos:7"},
|
||||
logs: []string{
|
||||
"unknown severity option: unknown severity: INVALID",
|
||||
},
|
||||
want: ReportConfig{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityUnknown},
|
||||
VulnType: []string{"os", "library"},
|
||||
Output: os.Stdout,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid option combination: --template enabled without --format",
|
||||
fields: fields{
|
||||
Template: "@contrib/gitlab.tpl",
|
||||
severities: "LOW",
|
||||
},
|
||||
args: []string{"gitlab/gitlab-ce:12.7.2-ce.0"},
|
||||
logs: []string{
|
||||
"--template is ignored because --format template is not specified. Use --template option with --format template option.",
|
||||
},
|
||||
want: ReportConfig{
|
||||
Output: os.Stdout,
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
|
||||
Template: "@contrib/gitlab.tpl",
|
||||
VulnType: []string{""},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid option combination: --template and --format json",
|
||||
fields: fields{
|
||||
Format: "json",
|
||||
Template: "@contrib/gitlab.tpl",
|
||||
severities: "LOW",
|
||||
},
|
||||
args: []string{"gitlab/gitlab-ce:12.7.2-ce.0"},
|
||||
logs: []string{
|
||||
"--template is ignored because --format json is specified. Use --template option with --format template option.",
|
||||
},
|
||||
want: ReportConfig{
|
||||
Format: "json",
|
||||
Output: os.Stdout,
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
|
||||
Template: "@contrib/gitlab.tpl",
|
||||
VulnType: []string{""},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid option combination: --format template without --template",
|
||||
fields: fields{
|
||||
Format: "template",
|
||||
severities: "LOW",
|
||||
},
|
||||
args: []string{"gitlab/gitlab-ce:12.7.2-ce.0"},
|
||||
logs: []string{
|
||||
"--format template is ignored because --template not is specified. Specify --template option when you use --format template.",
|
||||
},
|
||||
want: ReportConfig{
|
||||
Format: "template",
|
||||
Output: os.Stdout,
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
|
||||
VulnType: []string{""},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
core, obs := observer.New(zap.InfoLevel)
|
||||
logger := zap.New(core)
|
||||
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
_ = set.Parse(tt.args)
|
||||
|
||||
c := &ReportConfig{
|
||||
output: tt.fields.output,
|
||||
Format: tt.fields.Format,
|
||||
Template: tt.fields.Template,
|
||||
vulnType: tt.fields.vulnType,
|
||||
severities: tt.fields.severities,
|
||||
IgnoreFile: tt.fields.IgnoreFile,
|
||||
IgnoreUnfixed: tt.fields.IgnoreUnfixed,
|
||||
ExitCode: tt.fields.ExitCode,
|
||||
Output: tt.fields.Output,
|
||||
}
|
||||
|
||||
err := c.Init(logger.Sugar())
|
||||
|
||||
// tests log messages
|
||||
var gotMessages []string
|
||||
for _, entry := range obs.AllUntimed() {
|
||||
gotMessages = append(gotMessages, entry.Message)
|
||||
}
|
||||
assert.Equal(t, tt.logs, gotMessages, tt.name)
|
||||
|
||||
// test the error
|
||||
switch {
|
||||
case tt.wantErr != "":
|
||||
require.NotNil(t, err)
|
||||
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
|
||||
return
|
||||
default:
|
||||
assert.NoError(t, err, tt.name)
|
||||
}
|
||||
|
||||
assert.Equal(t, &tt.want, c, tt.name)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -51,8 +51,8 @@ func (c Cache) Reset() (err error) {
|
||||
if err := c.ClearDB(); err != nil {
|
||||
return xerrors.Errorf("failed to clear the database: %w", err)
|
||||
}
|
||||
if err := c.ClearImages(); err != nil {
|
||||
return xerrors.Errorf("failed to clear the image cache: %w", err)
|
||||
if err := c.ClearArtifacts(); err != nil {
|
||||
return xerrors.Errorf("failed to clear the artifact cache: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -66,9 +66,9 @@ func (c Cache) ClearDB() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearImages clears the cache images
|
||||
func (c Cache) ClearImages() error {
|
||||
log.Logger.Info("Removing image caches...")
|
||||
// ClearArtifacts clears the artifact cache
|
||||
func (c Cache) ClearArtifacts() error {
|
||||
log.Logger.Info("Removing artifact caches...")
|
||||
if err := c.Clear(); err != nil {
|
||||
return xerrors.Errorf("failed to remove the cache: %w", err)
|
||||
}
|
||||
@@ -96,7 +96,7 @@ func DownloadDB(appVersion, cacheDir string, quiet, light, skipUpdate bool) erro
|
||||
}
|
||||
|
||||
// for debug
|
||||
if err := showDBInfo(cacheDir); err != nil {
|
||||
if err = showDBInfo(cacheDir); err != nil {
|
||||
return xerrors.Errorf("failed to show database info: %w", err)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package config
|
||||
package option
|
||||
|
||||
import (
|
||||
"os"
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// ArtifactConfig holds the config for a artifact scanning
|
||||
type ArtifactConfig struct {
|
||||
// ArtifactOption holds the options for an artifact scanning
|
||||
type ArtifactOption struct {
|
||||
Input string
|
||||
Timeout time.Duration
|
||||
ClearCache bool
|
||||
@@ -22,9 +22,9 @@ type ArtifactConfig struct {
|
||||
Target string
|
||||
}
|
||||
|
||||
// NewArtifactConfig is the factory method to return artifact config
|
||||
func NewArtifactConfig(c *cli.Context) ArtifactConfig {
|
||||
return ArtifactConfig{
|
||||
// NewArtifactOption is the factory method to return artifact option
|
||||
func NewArtifactOption(c *cli.Context) ArtifactOption {
|
||||
return ArtifactOption{
|
||||
Input: c.String("input"),
|
||||
Timeout: c.Duration("timeout"),
|
||||
ClearCache: c.Bool("clear-cache"),
|
||||
@@ -34,7 +34,7 @@ func NewArtifactConfig(c *cli.Context) ArtifactConfig {
|
||||
}
|
||||
|
||||
// Init initialize the CLI context for artifact scanning
|
||||
func (c *ArtifactConfig) Init(ctx *cli.Context, logger *zap.SugaredLogger) (err error) {
|
||||
func (c *ArtifactOption) Init(ctx *cli.Context, logger *zap.SugaredLogger) (err error) {
|
||||
if c.Input == "" && ctx.Args().Len() == 0 {
|
||||
logger.Debug(`trivy requires at least 1 argument or --input option`)
|
||||
_ = cli.ShowSubcommandHelp(ctx) // nolint: errcheck
|
||||
@@ -1,10 +1,10 @@
|
||||
package config_test
|
||||
package option_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/commands/config"
|
||||
"github.com/aquasecurity/trivy/pkg/commands/option"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/urfave/cli/v2"
|
||||
@@ -12,18 +12,18 @@ import (
|
||||
"go.uber.org/zap/zaptest/observer"
|
||||
)
|
||||
|
||||
func TestArtifactConfig_Init(t *testing.T) {
|
||||
func TestArtifactOption_Init(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
logs []string
|
||||
want config.ArtifactConfig
|
||||
want option.ArtifactOption
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
args: []string{"alpine:3.10"},
|
||||
want: config.ArtifactConfig{
|
||||
want: option.ArtifactOption{
|
||||
Target: "alpine:3.10",
|
||||
},
|
||||
},
|
||||
@@ -46,7 +46,7 @@ func TestArtifactConfig_Init(t *testing.T) {
|
||||
ctx := cli.NewContext(app, set, nil)
|
||||
_ = set.Parse(tt.args)
|
||||
|
||||
c := config.NewArtifactConfig(ctx)
|
||||
c := option.NewArtifactOption(ctx)
|
||||
|
||||
err := c.Init(ctx, logger.Sugar())
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package config
|
||||
package option
|
||||
|
||||
import (
|
||||
"strings"
|
||||
@@ -7,20 +7,20 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// CacheConfig holds the config for cache
|
||||
type CacheConfig struct {
|
||||
// CacheOption holds the options for cache
|
||||
type CacheOption struct {
|
||||
CacheBackend string
|
||||
}
|
||||
|
||||
// NewCacheConfig returns an instance of CacheConfig
|
||||
func NewCacheConfig(c *cli.Context) CacheConfig {
|
||||
return CacheConfig{
|
||||
// NewCacheOption returns an instance of CacheOption
|
||||
func NewCacheOption(c *cli.Context) CacheOption {
|
||||
return CacheOption{
|
||||
CacheBackend: c.String("cache-backend"),
|
||||
}
|
||||
}
|
||||
|
||||
// Init initialize the CacheConfig
|
||||
func (c *CacheConfig) Init() error {
|
||||
// Init initialize the CacheOption
|
||||
func (c *CacheOption) Init() error {
|
||||
// "redis://" or "fs" are allowed for now
|
||||
// An empty value is also allowed for testability
|
||||
if !strings.HasPrefix(c.CacheBackend, "redis://") &&
|
||||
@@ -1,4 +1,4 @@
|
||||
package config_test
|
||||
package option_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
@@ -7,26 +7,26 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/commands/config"
|
||||
"github.com/aquasecurity/trivy/pkg/commands/option"
|
||||
)
|
||||
|
||||
func TestNewCacheConfig(t *testing.T) {
|
||||
func TestNewCacheOption(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
want config.CacheConfig
|
||||
want option.CacheOption
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
args: []string{"--cache-backend", "redis://localhost:6379"},
|
||||
want: config.CacheConfig{
|
||||
want: option.CacheOption{
|
||||
CacheBackend: "redis://localhost:6379",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default",
|
||||
args: []string{},
|
||||
want: config.CacheConfig{
|
||||
want: option.CacheOption{
|
||||
CacheBackend: "fs",
|
||||
},
|
||||
},
|
||||
@@ -40,13 +40,13 @@ func TestNewCacheConfig(t *testing.T) {
|
||||
c := cli.NewContext(app, set, nil)
|
||||
_ = set.Parse(tt.args)
|
||||
|
||||
got := config.NewCacheConfig(c)
|
||||
got := option.NewCacheOption(c)
|
||||
assert.Equal(t, tt.want, got, tt.name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheConfig_Init(t *testing.T) {
|
||||
func TestCacheOption_Init(t *testing.T) {
|
||||
type fields struct {
|
||||
backend string
|
||||
}
|
||||
@@ -77,7 +77,7 @@ func TestCacheConfig_Init(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &config.CacheConfig{
|
||||
c := &option.CacheOption{
|
||||
CacheBackend: tt.fields.backend,
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package config
|
||||
package option
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// DBConfig holds the config for trivy DB
|
||||
type DBConfig struct {
|
||||
// DBOption holds the options for trivy DB
|
||||
type DBOption struct {
|
||||
Reset bool
|
||||
DownloadDBOnly bool
|
||||
SkipUpdate bool
|
||||
@@ -14,9 +14,9 @@ type DBConfig struct {
|
||||
NoProgress bool
|
||||
}
|
||||
|
||||
// NewDBConfig is the factory method to return the DBConfig
|
||||
func NewDBConfig(c *cli.Context) DBConfig {
|
||||
return DBConfig{
|
||||
// NewDBOption is the factory method to return the DBOption
|
||||
func NewDBOption(c *cli.Context) DBOption {
|
||||
return DBOption{
|
||||
Reset: c.Bool("reset"),
|
||||
DownloadDBOnly: c.Bool("download-db-only"),
|
||||
SkipUpdate: c.Bool("skip-update"),
|
||||
@@ -25,8 +25,8 @@ func NewDBConfig(c *cli.Context) DBConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// Init initialize the DBConfig
|
||||
func (c *DBConfig) Init() (err error) {
|
||||
// Init initialize the DBOption
|
||||
func (c *DBOption) Init() (err error) {
|
||||
if c.SkipUpdate && c.DownloadDBOnly {
|
||||
return xerrors.New("--skip-update and --download-db-only options can not be specified both")
|
||||
}
|
||||
@@ -1,25 +1,24 @@
|
||||
package config_test
|
||||
package option_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/commands/option"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/commands/config"
|
||||
)
|
||||
|
||||
func TestNewDBConfig(t *testing.T) {
|
||||
func TestNewDBOption(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
want config.DBConfig
|
||||
want option.DBOption
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
args: []string{"--reset", "--skip-update"},
|
||||
want: config.DBConfig{
|
||||
want: option.DBOption{
|
||||
Reset: true,
|
||||
SkipUpdate: true,
|
||||
},
|
||||
@@ -35,13 +34,13 @@ func TestNewDBConfig(t *testing.T) {
|
||||
c := cli.NewContext(app, set, nil)
|
||||
_ = set.Parse(tt.args)
|
||||
|
||||
got := config.NewDBConfig(c)
|
||||
got := option.NewDBOption(c)
|
||||
assert.Equal(t, tt.want, got, tt.name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDBConfig_Init(t *testing.T) {
|
||||
func TestDBOption_Init(t *testing.T) {
|
||||
type fields struct {
|
||||
Reset bool
|
||||
DownloadDBOnly bool
|
||||
@@ -70,7 +69,7 @@ func TestDBConfig_Init(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &config.DBConfig{
|
||||
c := &option.DBOption{
|
||||
Reset: tt.fields.Reset,
|
||||
DownloadDBOnly: tt.fields.DownloadDBOnly,
|
||||
SkipUpdate: tt.fields.SkipUpdate,
|
||||
@@ -1,4 +1,4 @@
|
||||
package config
|
||||
package option
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli/v2"
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
)
|
||||
|
||||
// GlobalConfig holds the global config for trivy
|
||||
type GlobalConfig struct {
|
||||
// GlobalOption holds the global options for trivy
|
||||
type GlobalOption struct {
|
||||
Context *cli.Context
|
||||
Logger *zap.SugaredLogger
|
||||
|
||||
@@ -19,16 +19,16 @@ type GlobalConfig struct {
|
||||
CacheDir string
|
||||
}
|
||||
|
||||
// NewGlobalConfig is the factory method to return GlobalConfig
|
||||
func NewGlobalConfig(c *cli.Context) (GlobalConfig, error) {
|
||||
// NewGlobalOption is the factory method to return GlobalOption
|
||||
func NewGlobalOption(c *cli.Context) (GlobalOption, error) {
|
||||
quiet := c.Bool("quiet")
|
||||
debug := c.Bool("debug")
|
||||
logger, err := log.NewLogger(debug, quiet)
|
||||
if err != nil {
|
||||
return GlobalConfig{}, xerrors.New("failed to create a logger")
|
||||
return GlobalOption{}, xerrors.New("failed to create a logger")
|
||||
}
|
||||
|
||||
return GlobalConfig{
|
||||
return GlobalOption{
|
||||
Context: c,
|
||||
Logger: logger,
|
||||
|
||||
@@ -1,26 +1,25 @@
|
||||
package config_test
|
||||
package option_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"testing"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/commands/option"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/commands/config"
|
||||
)
|
||||
|
||||
func TestNewGlobalConfig(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
want config.GlobalConfig
|
||||
want option.GlobalOption
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
args: []string{"--quiet", "--debug"},
|
||||
want: config.GlobalConfig{
|
||||
want: option.GlobalOption{
|
||||
Quiet: true,
|
||||
Debug: true,
|
||||
},
|
||||
@@ -36,7 +35,7 @@ func TestNewGlobalConfig(t *testing.T) {
|
||||
c := cli.NewContext(app, set, nil)
|
||||
_ = set.Parse(tt.args)
|
||||
|
||||
got, err := config.NewGlobalConfig(c)
|
||||
got, err := option.NewGlobalOption(c)
|
||||
require.NoError(t, err, err)
|
||||
assert.Equal(t, tt.want.Quiet, got.Quiet, tt.name)
|
||||
assert.Equal(t, tt.want.Debug, got.Debug, tt.name)
|
||||
19
pkg/commands/option/image.go
Normal file
19
pkg/commands/option/image.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// ImageOption holds the options for scanning images
|
||||
type ImageOption struct {
|
||||
ScanRemovedPkgs bool
|
||||
ListAllPkgs bool
|
||||
}
|
||||
|
||||
// NewImageOption is the factory method to return ImageOption
|
||||
func NewImageOption(c *cli.Context) ImageOption {
|
||||
return ImageOption{
|
||||
ScanRemovedPkgs: c.Bool("removed-pkgs"),
|
||||
ListAllPkgs: c.Bool("list-all-pkgs"),
|
||||
}
|
||||
}
|
||||
127
pkg/commands/option/report.go
Normal file
127
pkg/commands/option/report.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
)
|
||||
|
||||
// ReportOption holds the options for reporting scan results
|
||||
type ReportOption struct {
|
||||
Format string
|
||||
Template string
|
||||
|
||||
IgnoreFile string
|
||||
IgnoreUnfixed bool
|
||||
ExitCode int
|
||||
IgnorePolicy string
|
||||
|
||||
// these variables are not exported
|
||||
vulnType string
|
||||
securityChecks string
|
||||
output string
|
||||
severities string
|
||||
|
||||
// these variables are populated by Init()
|
||||
VulnType []string
|
||||
SecurityChecks []string
|
||||
Output *os.File
|
||||
Severities []dbTypes.Severity
|
||||
}
|
||||
|
||||
// NewReportOption is the factory method to return ReportOption
|
||||
func NewReportOption(c *cli.Context) ReportOption {
|
||||
return ReportOption{
|
||||
output: c.String("output"),
|
||||
Format: c.String("format"),
|
||||
Template: c.String("template"),
|
||||
IgnorePolicy: c.String("ignore-policy"),
|
||||
|
||||
vulnType: c.String("vuln-type"),
|
||||
securityChecks: c.String("security-checks"),
|
||||
severities: c.String("severity"),
|
||||
IgnoreFile: c.String("ignorefile"),
|
||||
IgnoreUnfixed: c.Bool("ignore-unfixed"),
|
||||
ExitCode: c.Int("exit-code"),
|
||||
}
|
||||
}
|
||||
|
||||
// Init initializes the ReportOption
|
||||
func (c *ReportOption) Init(logger *zap.SugaredLogger) error {
|
||||
var err error
|
||||
|
||||
if c.Template != "" {
|
||||
if c.Format == "" {
|
||||
logger.Warn("--template is ignored because --format template is not specified. Use --template option with --format template option.")
|
||||
} else if c.Format != "template" {
|
||||
logger.Warnf("--template is ignored because --format %s is specified. Use --template option with --format template option.", c.Format)
|
||||
}
|
||||
}
|
||||
if c.Format == "template" && c.Template == "" {
|
||||
logger.Warn("--format template is ignored because --template not is specified. Specify --template option when you use --format template.")
|
||||
}
|
||||
|
||||
c.Severities = splitSeverity(logger, c.severities)
|
||||
|
||||
if err = c.populateVulnTypes(); err != nil {
|
||||
return xerrors.Errorf("vuln type: %w", err)
|
||||
}
|
||||
|
||||
if err = c.populateSecurityChecks(); err != nil {
|
||||
return xerrors.Errorf("security checks: %w", err)
|
||||
}
|
||||
|
||||
// for testability
|
||||
c.severities = ""
|
||||
c.vulnType = ""
|
||||
c.securityChecks = ""
|
||||
|
||||
c.Output = os.Stdout
|
||||
if c.output != "" {
|
||||
if c.Output, err = os.Create(c.output); err != nil {
|
||||
return xerrors.Errorf("failed to create an output file: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ReportOption) populateVulnTypes() error {
|
||||
for _, v := range strings.Split(c.vulnType, ",") {
|
||||
if types.NewVulnType(v) == types.VulnTypeUnknown {
|
||||
return xerrors.Errorf("unknown vulnerability type (%s)", v)
|
||||
}
|
||||
c.VulnType = append(c.VulnType, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ReportOption) populateSecurityChecks() error {
|
||||
for _, v := range strings.Split(c.securityChecks, ",") {
|
||||
if types.NewSecurityCheck(v) == types.SecurityCheckUnknown {
|
||||
return xerrors.Errorf("unknown security check (%s)", v)
|
||||
}
|
||||
c.SecurityChecks = append(c.SecurityChecks, v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func splitSeverity(logger *zap.SugaredLogger, severity string) []dbTypes.Severity {
|
||||
logger.Debugf("Severities: %s", severity)
|
||||
var severities []dbTypes.Severity
|
||||
for _, s := range strings.Split(severity, ",") {
|
||||
severity, err := dbTypes.NewSeverity(s)
|
||||
if err != nil {
|
||||
logger.Warnf("unknown severity option: %s", err)
|
||||
}
|
||||
severities = append(severities, severity)
|
||||
}
|
||||
return severities
|
||||
}
|
||||
178
pkg/commands/option/report_test.go
Normal file
178
pkg/commands/option/report_test.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package option
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zaptest/observer"
|
||||
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
)
|
||||
|
||||
func TestReportReportConfig_Init(t *testing.T) {
|
||||
type fields struct {
|
||||
output string
|
||||
Format string
|
||||
Template string
|
||||
vulnType string
|
||||
securityChecks string
|
||||
severities string
|
||||
IgnoreFile string
|
||||
IgnoreUnfixed bool
|
||||
ExitCode int
|
||||
VulnType []string
|
||||
Output *os.File
|
||||
Severities []dbTypes.Severity
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args []string
|
||||
logs []string
|
||||
want ReportOption
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
fields: fields{
|
||||
severities: "CRITICAL",
|
||||
vulnType: "os",
|
||||
securityChecks: "vuln",
|
||||
},
|
||||
args: []string{"alpine:3.10"},
|
||||
want: ReportOption{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical},
|
||||
VulnType: []string{types.VulnTypeOS},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
Output: os.Stdout,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path with an unknown severity",
|
||||
fields: fields{
|
||||
severities: "CRITICAL,INVALID",
|
||||
vulnType: "os,library",
|
||||
securityChecks: "vuln",
|
||||
},
|
||||
args: []string{"centos:7"},
|
||||
logs: []string{
|
||||
"unknown severity option: unknown severity: INVALID",
|
||||
},
|
||||
want: ReportOption{
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityUnknown},
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
Output: os.Stdout,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid option combination: --template enabled without --format",
|
||||
fields: fields{
|
||||
Template: "@contrib/gitlab.tpl",
|
||||
severities: "LOW",
|
||||
vulnType: "os",
|
||||
securityChecks: "vuln",
|
||||
},
|
||||
args: []string{"gitlab/gitlab-ce:12.7.2-ce.0"},
|
||||
logs: []string{
|
||||
"--template is ignored because --format template is not specified. Use --template option with --format template option.",
|
||||
},
|
||||
want: ReportOption{
|
||||
Output: os.Stdout,
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
|
||||
Template: "@contrib/gitlab.tpl",
|
||||
VulnType: []string{types.VulnTypeOS},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid option combination: --template and --format json",
|
||||
fields: fields{
|
||||
Format: "json",
|
||||
Template: "@contrib/gitlab.tpl",
|
||||
severities: "LOW",
|
||||
vulnType: "os",
|
||||
securityChecks: "vuln",
|
||||
},
|
||||
args: []string{"gitlab/gitlab-ce:12.7.2-ce.0"},
|
||||
logs: []string{
|
||||
"--template is ignored because --format json is specified. Use --template option with --format template option.",
|
||||
},
|
||||
want: ReportOption{
|
||||
Format: "json",
|
||||
Output: os.Stdout,
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
|
||||
Template: "@contrib/gitlab.tpl",
|
||||
VulnType: []string{types.VulnTypeOS},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid option combination: --format template without --template",
|
||||
fields: fields{
|
||||
Format: "template",
|
||||
severities: "LOW",
|
||||
vulnType: "os",
|
||||
securityChecks: "vuln",
|
||||
},
|
||||
args: []string{"gitlab/gitlab-ce:12.7.2-ce.0"},
|
||||
logs: []string{
|
||||
"--format template is ignored because --template not is specified. Specify --template option when you use --format template.",
|
||||
},
|
||||
want: ReportOption{
|
||||
Format: "template",
|
||||
Output: os.Stdout,
|
||||
Severities: []dbTypes.Severity{dbTypes.SeverityLow},
|
||||
VulnType: []string{types.VulnTypeOS},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
core, obs := observer.New(zap.InfoLevel)
|
||||
logger := zap.New(core)
|
||||
|
||||
set := flag.NewFlagSet("test", 0)
|
||||
_ = set.Parse(tt.args)
|
||||
|
||||
c := &ReportOption{
|
||||
output: tt.fields.output,
|
||||
Format: tt.fields.Format,
|
||||
Template: tt.fields.Template,
|
||||
vulnType: tt.fields.vulnType,
|
||||
securityChecks: tt.fields.securityChecks,
|
||||
severities: tt.fields.severities,
|
||||
IgnoreFile: tt.fields.IgnoreFile,
|
||||
IgnoreUnfixed: tt.fields.IgnoreUnfixed,
|
||||
ExitCode: tt.fields.ExitCode,
|
||||
Output: tt.fields.Output,
|
||||
}
|
||||
|
||||
err := c.Init(logger.Sugar())
|
||||
|
||||
// tests log messages
|
||||
var gotMessages []string
|
||||
for _, entry := range obs.AllUntimed() {
|
||||
gotMessages = append(gotMessages, entry.Message)
|
||||
}
|
||||
assert.Equal(t, tt.logs, gotMessages, tt.name)
|
||||
|
||||
// test the error
|
||||
switch {
|
||||
case tt.wantErr != "":
|
||||
require.NotNil(t, err)
|
||||
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, err, tt.name)
|
||||
assert.Equal(t, &tt.want, c, tt.name)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/urfave/cli/v2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/commands/config"
|
||||
"github.com/aquasecurity/trivy/pkg/commands/option"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/plugin"
|
||||
)
|
||||
@@ -105,7 +105,7 @@ func LoadCommands() cli.Commands {
|
||||
}
|
||||
|
||||
func initLogger(ctx *cli.Context) error {
|
||||
conf, err := config.NewGlobalConfig(ctx)
|
||||
conf, err := option.NewGlobalOption(ctx)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("config error: %w", err)
|
||||
}
|
||||
|
||||
@@ -3,14 +3,14 @@ package server
|
||||
import (
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/commands/config"
|
||||
"github.com/aquasecurity/trivy/pkg/commands/option"
|
||||
)
|
||||
|
||||
// Config holds the Trivy config
|
||||
type Config struct {
|
||||
config.GlobalConfig
|
||||
config.DBConfig
|
||||
config.CacheConfig
|
||||
option.GlobalOption
|
||||
option.DBOption
|
||||
option.CacheOption
|
||||
|
||||
Listen string
|
||||
Token string
|
||||
@@ -20,11 +20,11 @@ type Config struct {
|
||||
// NewConfig is the factory method to return config
|
||||
func NewConfig(c *cli.Context) Config {
|
||||
// the error is ignored because logger is unnecessary
|
||||
gc, _ := config.NewGlobalConfig(c) // nolint: errcheck
|
||||
gc, _ := option.NewGlobalOption(c) // nolint: errcheck
|
||||
return Config{
|
||||
GlobalConfig: gc,
|
||||
DBConfig: config.NewDBConfig(c),
|
||||
CacheConfig: config.NewCacheConfig(c),
|
||||
GlobalOption: gc,
|
||||
DBOption: option.NewDBOption(c),
|
||||
CacheOption: option.NewCacheOption(c),
|
||||
|
||||
Listen: c.String("listen"),
|
||||
Token: c.String("token"),
|
||||
@@ -34,10 +34,10 @@ func NewConfig(c *cli.Context) Config {
|
||||
|
||||
// Init initializes the config
|
||||
func (c *Config) Init() (err error) {
|
||||
if err := c.DBConfig.Init(); err != nil {
|
||||
if err := c.DBOption.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.CacheConfig.Init(); err != nil {
|
||||
if err := c.CacheOption.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/commands/config"
|
||||
"github.com/aquasecurity/trivy/pkg/commands/option"
|
||||
"github.com/aquasecurity/trivy/pkg/commands/server"
|
||||
)
|
||||
|
||||
@@ -22,10 +22,10 @@ func TestNew(t *testing.T) {
|
||||
name: "happy path",
|
||||
args: []string{"-quiet", "--no-progress", "--reset", "--skip-update", "--listen", "localhost:8080"},
|
||||
want: server.Config{
|
||||
GlobalConfig: config.GlobalConfig{
|
||||
GlobalOption: option.GlobalOption{
|
||||
Quiet: true,
|
||||
},
|
||||
DBConfig: config.DBConfig{
|
||||
DBOption: option.DBOption{
|
||||
Reset: true,
|
||||
SkipUpdate: true,
|
||||
NoProgress: true,
|
||||
@@ -47,11 +47,11 @@ func TestNew(t *testing.T) {
|
||||
ctx := cli.NewContext(app, set, nil)
|
||||
_ = set.Parse(tt.args)
|
||||
|
||||
tt.want.GlobalConfig.Context = ctx
|
||||
tt.want.GlobalOption.Context = ctx
|
||||
|
||||
got := server.NewConfig(ctx)
|
||||
assert.Equal(t, tt.want.GlobalConfig.Quiet, got.Quiet, tt.name)
|
||||
assert.Equal(t, tt.want.DBConfig, got.DBConfig, tt.name)
|
||||
assert.Equal(t, tt.want.GlobalOption.Quiet, got.Quiet, tt.name)
|
||||
assert.Equal(t, tt.want.DBOption, got.DBOption, tt.name)
|
||||
assert.Equal(t, tt.want.Listen, got.Listen, tt.name)
|
||||
})
|
||||
}
|
||||
@@ -60,8 +60,8 @@ func TestNew(t *testing.T) {
|
||||
func TestConfig_Init(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
globalConfig config.GlobalConfig
|
||||
dbConfig config.DBConfig
|
||||
globalConfig option.GlobalOption
|
||||
dbConfig option.DBOption
|
||||
args []string
|
||||
wantErr string
|
||||
}{
|
||||
@@ -71,14 +71,14 @@ func TestConfig_Init(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "happy path: reset",
|
||||
dbConfig: config.DBConfig{
|
||||
dbConfig: option.DBOption{
|
||||
Reset: true,
|
||||
},
|
||||
args: []string{"alpine:3.10"},
|
||||
},
|
||||
{
|
||||
name: "sad: skip and download db",
|
||||
dbConfig: config.DBConfig{
|
||||
dbConfig: option.DBOption{
|
||||
SkipUpdate: true,
|
||||
DownloadDBOnly: true,
|
||||
},
|
||||
@@ -89,7 +89,7 @@ func TestConfig_Init(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &server.Config{
|
||||
DBConfig: tt.dbConfig,
|
||||
DBOption: tt.dbConfig,
|
||||
}
|
||||
|
||||
err := c.Init()
|
||||
|
||||
@@ -3,7 +3,7 @@ package library
|
||||
import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer/library"
|
||||
ftypes "github.com/aquasecurity/fanal/types"
|
||||
ecosystem "github.com/aquasecurity/trivy-db/pkg/vulnsrc/ghsa"
|
||||
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
|
||||
"github.com/aquasecurity/trivy/pkg/detector/library/bundler"
|
||||
@@ -25,21 +25,21 @@ type advisory interface {
|
||||
func NewDriver(libType string) (Driver, error) {
|
||||
var driver Driver
|
||||
switch libType {
|
||||
case library.Bundler:
|
||||
case ftypes.Bundler:
|
||||
driver = newRubyGemsDriver()
|
||||
case library.Cargo:
|
||||
case ftypes.Cargo:
|
||||
driver = newCargoDriver()
|
||||
case library.Composer:
|
||||
case ftypes.Composer:
|
||||
driver = newComposerDriver()
|
||||
case library.Npm, library.Yarn:
|
||||
case ftypes.Npm, ftypes.Yarn:
|
||||
driver = newNpmDriver()
|
||||
case library.Pipenv, library.Poetry:
|
||||
case ftypes.Pipenv, ftypes.Poetry:
|
||||
driver = newPipDriver()
|
||||
case library.NuGet:
|
||||
case ftypes.NuGet:
|
||||
driver = newNugetDriver()
|
||||
case library.Jar:
|
||||
case ftypes.Jar:
|
||||
driver = newMavenDriver()
|
||||
case library.GoBinary:
|
||||
case ftypes.GoBinary, ftypes.GoMod:
|
||||
driver = Driver{
|
||||
ecosystem: vulnerability.Go,
|
||||
advisories: []advisory{NewAdvisory(vulnerability.Go, comparer.GenericComparer{})},
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
lib "github.com/aquasecurity/fanal/analyzer/library"
|
||||
ftypes "github.com/aquasecurity/fanal/types"
|
||||
"github.com/aquasecurity/trivy-db/pkg/db"
|
||||
"github.com/aquasecurity/trivy/pkg/dbtest"
|
||||
"github.com/aquasecurity/trivy/pkg/detector/library"
|
||||
@@ -29,7 +29,7 @@ func TestDriver_Detect(t *testing.T) {
|
||||
{
|
||||
name: "happy path",
|
||||
fixtures: []string{"testdata/fixtures/php.yaml"},
|
||||
libType: lib.Composer,
|
||||
libType: ftypes.Composer,
|
||||
args: args{
|
||||
pkgName: "symfony/symfony",
|
||||
pkgVer: "4.2.6",
|
||||
@@ -46,7 +46,7 @@ func TestDriver_Detect(t *testing.T) {
|
||||
{
|
||||
name: "non-prefix buckets",
|
||||
fixtures: []string{"testdata/fixtures/php-without-prefix.yaml"},
|
||||
libType: lib.Composer,
|
||||
libType: ftypes.Composer,
|
||||
args: args{
|
||||
pkgName: "symfony/symfony",
|
||||
pkgVer: "4.2.6",
|
||||
@@ -63,7 +63,7 @@ func TestDriver_Detect(t *testing.T) {
|
||||
{
|
||||
name: "no patched versions in the advisory",
|
||||
fixtures: []string{"testdata/fixtures/php.yaml"},
|
||||
libType: lib.Composer,
|
||||
libType: ftypes.Composer,
|
||||
args: args{
|
||||
pkgName: "symfony/symfony",
|
||||
pkgVer: "4.4.6",
|
||||
@@ -80,7 +80,7 @@ func TestDriver_Detect(t *testing.T) {
|
||||
{
|
||||
name: "no vulnerable versions in the advisory",
|
||||
fixtures: []string{"testdata/fixtures/ruby.yaml"},
|
||||
libType: lib.Bundler,
|
||||
libType: ftypes.Bundler,
|
||||
args: args{
|
||||
pkgName: "activesupport",
|
||||
pkgVer: "4.1.1",
|
||||
@@ -97,7 +97,7 @@ func TestDriver_Detect(t *testing.T) {
|
||||
{
|
||||
name: "no vulnerability",
|
||||
fixtures: []string{"testdata/fixtures/php.yaml"},
|
||||
libType: lib.Composer,
|
||||
libType: ftypes.Composer,
|
||||
args: args{
|
||||
pkgName: "symfony/symfony",
|
||||
pkgVer: "4.4.7",
|
||||
|
||||
56
pkg/downloader/download.go
Normal file
56
pkg/downloader/download.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package downloader
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
getter "github.com/hashicorp/go-getter"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// DownloadToTempDir downloads the configured source to a temp dir.
|
||||
func DownloadToTempDir(ctx context.Context, url string) (string, error) {
|
||||
tempDir, err := os.MkdirTemp("", "trivy-plugin")
|
||||
if err != nil {
|
||||
return "", xerrors.Errorf("failed to create a temp dir: %w", err)
|
||||
}
|
||||
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", xerrors.Errorf("unable to get the current dir: %w", err)
|
||||
}
|
||||
|
||||
if err = Download(ctx, url, tempDir, pwd); err != nil {
|
||||
return "", xerrors.Errorf("download error: %w", err)
|
||||
}
|
||||
|
||||
return tempDir, nil
|
||||
}
|
||||
|
||||
// Download downloads the configured source to the destination.
|
||||
func Download(ctx context.Context, src, dst, pwd string) error {
|
||||
// go-getter doesn't allow the dst directory already exists if the src is directory.
|
||||
_ = os.RemoveAll(dst)
|
||||
|
||||
var opts []getter.ClientOption
|
||||
|
||||
// Overwrite the file getter so that a file will be copied
|
||||
getter.Getters["file"] = &getter.FileGetter{Copy: true}
|
||||
|
||||
// Build the client
|
||||
client := &getter.Client{
|
||||
Ctx: ctx,
|
||||
Src: src,
|
||||
Dst: dst,
|
||||
Pwd: pwd,
|
||||
Getters: getter.Getters,
|
||||
Mode: getter.ClientModeAny,
|
||||
Options: opts,
|
||||
}
|
||||
|
||||
if err := client.Get(); err != nil {
|
||||
return xerrors.Errorf("failed to download: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-github/v28/github"
|
||||
"github.com/google/go-github/v33/github"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
@@ -44,7 +44,7 @@ func (r Repository) ListReleases(ctx context.Context, opt *github.ListOptions) (
|
||||
|
||||
// DownloadAsset returns reader object of downloaded object
|
||||
func (r Repository) DownloadAsset(ctx context.Context, id int64) (io.ReadCloser, string, error) {
|
||||
return r.repository.DownloadReleaseAsset(ctx, r.owner, r.repoName, id)
|
||||
return r.repository.DownloadReleaseAsset(ctx, r.owner, r.repoName, id, nil)
|
||||
}
|
||||
|
||||
// Operation defines the file operations
|
||||
@@ -113,7 +113,7 @@ func (c Client) DownloadDB(ctx context.Context, fileName string) (io.ReadCloser,
|
||||
return nil, 0, xerrors.New("DB file not found")
|
||||
}
|
||||
|
||||
func (c Client) downloadAsset(ctx context.Context, asset github.ReleaseAsset, fileName string) (io.ReadCloser, int, error) {
|
||||
func (c Client) downloadAsset(ctx context.Context, asset *github.ReleaseAsset, fileName string) (io.ReadCloser, int, error) {
|
||||
log.Logger.Debugf("asset name: %s", asset.GetName())
|
||||
if asset.GetName() != fileName {
|
||||
return nil, 0, xerrors.New("file name doesn't match")
|
||||
|
||||
@@ -13,12 +13,10 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/google/go-github/v33/github"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/google/go-github/v28/github"
|
||||
"github.com/stretchr/testify/mock"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type MockRepository struct {
|
||||
@@ -96,7 +94,7 @@ func TestClient_DownloadDB(t *testing.T) {
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2020, 1, 1, 1, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Assets: []github.ReleaseAsset{
|
||||
Assets: []*github.ReleaseAsset{
|
||||
{
|
||||
ID: github.Int64(200),
|
||||
Name: github.String("trivy.db.gz"),
|
||||
@@ -109,7 +107,7 @@ func TestClient_DownloadDB(t *testing.T) {
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Assets: []github.ReleaseAsset{
|
||||
Assets: []*github.ReleaseAsset{
|
||||
{
|
||||
ID: github.Int64(100),
|
||||
Name: github.String("trivy.db.gz"),
|
||||
@@ -143,7 +141,7 @@ func TestClient_DownloadDB(t *testing.T) {
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Assets: []github.ReleaseAsset{
|
||||
Assets: []*github.ReleaseAsset{
|
||||
{
|
||||
ID: github.Int64(100),
|
||||
Name: github.String("trivy.db.gz"),
|
||||
@@ -177,7 +175,7 @@ func TestClient_DownloadDB(t *testing.T) {
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2019, 10, 1, 23, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Assets: []github.ReleaseAsset{
|
||||
Assets: []*github.ReleaseAsset{
|
||||
{
|
||||
ID: github.Int64(100),
|
||||
Name: github.String("trivy.db.gz"),
|
||||
@@ -191,7 +189,7 @@ func TestClient_DownloadDB(t *testing.T) {
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2019, 10, 2, 0, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Assets: []github.ReleaseAsset{
|
||||
Assets: []*github.ReleaseAsset{
|
||||
{
|
||||
ID: github.Int64(300),
|
||||
Name: github.String("trivy.db.gz"),
|
||||
@@ -204,7 +202,7 @@ func TestClient_DownloadDB(t *testing.T) {
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2019, 10, 1, 22, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Assets: []github.ReleaseAsset{
|
||||
Assets: []*github.ReleaseAsset{
|
||||
{
|
||||
ID: github.Int64(200),
|
||||
Name: github.String("trivy.db.gz"),
|
||||
@@ -255,7 +253,7 @@ func TestClient_DownloadDB(t *testing.T) {
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2019, 10, 1, 22, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Assets: []github.ReleaseAsset{
|
||||
Assets: []*github.ReleaseAsset{
|
||||
{
|
||||
ID: github.Int64(200),
|
||||
Name: github.String("trivy.db.gz"),
|
||||
@@ -310,7 +308,7 @@ func TestClient_DownloadDB(t *testing.T) {
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Assets: []github.ReleaseAsset{
|
||||
Assets: []*github.ReleaseAsset{
|
||||
{
|
||||
ID: github.Int64(100),
|
||||
Name: github.String("trivy.db.gz"),
|
||||
@@ -350,7 +348,7 @@ func TestClient_DownloadDB(t *testing.T) {
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Assets: []github.ReleaseAsset{
|
||||
Assets: []*github.ReleaseAsset{
|
||||
{
|
||||
ID: github.Int64(100),
|
||||
Name: github.String("trivy.db.gz"),
|
||||
@@ -385,7 +383,7 @@ func TestClient_DownloadDB(t *testing.T) {
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Assets: []github.ReleaseAsset{
|
||||
Assets: []*github.ReleaseAsset{
|
||||
{
|
||||
ID: github.Int64(100),
|
||||
Name: github.String("trivy.db.gz"),
|
||||
|
||||
@@ -7,7 +7,8 @@ import (
|
||||
"go.uber.org/zap/zapcore"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/go-dep-parser/pkg/log"
|
||||
flog "github.com/aquasecurity/fanal/log"
|
||||
dlog "github.com/aquasecurity/go-dep-parser/pkg/log"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -30,7 +31,10 @@ func InitLogger(debug, disable bool) (err error) {
|
||||
}
|
||||
|
||||
// Set logger for go-dep-parser
|
||||
log.SetLogger(Logger)
|
||||
dlog.SetLogger(Logger)
|
||||
|
||||
// Set logger for fanal
|
||||
flog.SetLogger(Logger)
|
||||
|
||||
return nil
|
||||
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
getter "github.com/hashicorp/go-getter"
|
||||
"golang.org/x/xerrors"
|
||||
yaml "gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/downloader"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/utils"
|
||||
)
|
||||
@@ -116,7 +116,7 @@ func (p Plugin) install(ctx context.Context, dst, pwd string) error {
|
||||
}
|
||||
|
||||
log.Logger.Debugf("Downloading the execution file from %s...", platform.URI)
|
||||
if err = download(ctx, platform.URI, dst, pwd); err != nil {
|
||||
if err = downloader.Download(ctx, platform.URI, dst, pwd); err != nil {
|
||||
return xerrors.Errorf("unable to download the execution file (%s): %w", platform.URI, err)
|
||||
}
|
||||
return nil
|
||||
@@ -147,7 +147,7 @@ func Install(ctx context.Context, url string, force bool) (Plugin, error) {
|
||||
}
|
||||
|
||||
log.Logger.Infof("Installing the plugin from %s...", url)
|
||||
tempDir, err := downloadToTempDir(ctx, url)
|
||||
tempDir, err := downloader.DownloadToTempDir(ctx, url)
|
||||
if err != nil {
|
||||
return Plugin{}, xerrors.Errorf("download failed: %w", err)
|
||||
}
|
||||
@@ -182,51 +182,6 @@ func Uninstall(name string) error {
|
||||
return os.RemoveAll(pluginDir)
|
||||
}
|
||||
|
||||
func downloadToTempDir(ctx context.Context, url string) (string, error) {
|
||||
tempDir, err := os.MkdirTemp("", "trivy-plugin")
|
||||
if err != nil {
|
||||
return "", xerrors.Errorf("failed to create a temp dir: %w", err)
|
||||
}
|
||||
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", xerrors.Errorf("unable to get the current dir: %w", err)
|
||||
}
|
||||
|
||||
if err = download(ctx, url, tempDir, pwd); err != nil {
|
||||
return "", xerrors.Errorf("download error: %w", err)
|
||||
}
|
||||
|
||||
return tempDir, nil
|
||||
}
|
||||
|
||||
func download(ctx context.Context, src, dst, pwd string) error {
|
||||
// go-getter doesn't allow the dst directory already exists if the src is directory.
|
||||
_ = os.RemoveAll(dst)
|
||||
|
||||
var opts []getter.ClientOption
|
||||
|
||||
// Overwrite the file getter so that a file will be copied
|
||||
getter.Getters["file"] = &getter.FileGetter{Copy: true}
|
||||
|
||||
// Build the client
|
||||
client := &getter.Client{
|
||||
Ctx: ctx,
|
||||
Src: src,
|
||||
Dst: dst,
|
||||
Pwd: pwd,
|
||||
Getters: getter.Getters,
|
||||
Mode: getter.ClientModeAny,
|
||||
Options: opts,
|
||||
}
|
||||
|
||||
if err := client.Get(); err != nil {
|
||||
return xerrors.Errorf("failed to download: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadAll loads all plugins
|
||||
func LoadAll() ([]Plugin, error) {
|
||||
pluginsDir := dir()
|
||||
|
||||
27
pkg/report/json.go
Normal file
27
pkg/report/json.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// JSONWriter implements result Writer
|
||||
type JSONWriter struct {
|
||||
Output io.Writer
|
||||
}
|
||||
|
||||
// Write writes the results in JSON format
|
||||
func (jw JSONWriter) Write(results Results) error {
|
||||
output, err := json.MarshalIndent(results, "", " ")
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to marshal json: %w", err)
|
||||
}
|
||||
|
||||
if _, err = fmt.Fprint(jw.Output, string(output)); err != nil {
|
||||
return xerrors.Errorf("failed to write json: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
82
pkg/report/json_test.go
Normal file
82
pkg/report/json_test.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package report_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/report"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
)
|
||||
|
||||
func TestReportWriter_JSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
detectedVulns []types.DetectedVulnerability
|
||||
expectedJSON report.Results
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
detectedVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2020-0001",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "3.4.5",
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "foobar",
|
||||
Description: "baz",
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedJSON: report.Results{
|
||||
report.Result{
|
||||
Target: "foojson",
|
||||
Vulnerabilities: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2020-0001",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "3.4.5",
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "foobar",
|
||||
Description: "baz",
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
jw := report.JSONWriter{}
|
||||
jsonWritten := bytes.Buffer{}
|
||||
jw.Output = &jsonWritten
|
||||
|
||||
inputResults := report.Results{
|
||||
{
|
||||
Target: "foojson",
|
||||
Vulnerabilities: tc.detectedVulns,
|
||||
},
|
||||
}
|
||||
|
||||
err := report.WriteResults("json", &jsonWritten, nil, inputResults, "", false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
writtenResults := report.Results{}
|
||||
err = json.Unmarshal([]byte(jsonWritten.String()), &writtenResults)
|
||||
assert.NoError(t, err, "invalid json written", tc.name)
|
||||
|
||||
assert.Equal(t, tc.expectedJSON, writtenResults, tc.name)
|
||||
})
|
||||
}
|
||||
}
|
||||
113
pkg/report/table.go
Normal file
113
pkg/report/table.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
ftypes "github.com/aquasecurity/fanal/types"
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/utils"
|
||||
)
|
||||
|
||||
// TableWriter implements Writer and output in tabular form
|
||||
type TableWriter struct {
|
||||
Severities []dbTypes.Severity
|
||||
Output io.Writer
|
||||
Light bool
|
||||
}
|
||||
|
||||
// Write writes the result on standard output
|
||||
func (tw TableWriter) Write(results Results) error {
|
||||
for _, result := range results {
|
||||
// Skip zero vulnerabilities on Java archives (JAR/WAR/EAR)
|
||||
if result.Type == ftypes.Jar && len(result.Vulnerabilities) == 0 {
|
||||
continue
|
||||
}
|
||||
tw.write(result)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tw TableWriter) write(result Result) {
|
||||
table := tablewriter.NewWriter(tw.Output)
|
||||
|
||||
total, severityCount := tw.writeVulnerabilities(table, result.Vulnerabilities)
|
||||
|
||||
var severities []string
|
||||
for _, sev := range tw.Severities {
|
||||
severities = append(severities, sev.String())
|
||||
}
|
||||
|
||||
var results []string
|
||||
for _, severity := range dbTypes.SeverityNames {
|
||||
if !utils.StringInSlice(severity, severities) {
|
||||
continue
|
||||
}
|
||||
r := fmt.Sprintf("%s: %d", severity, severityCount[severity])
|
||||
results = append(results, r)
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s\n", result.Target)
|
||||
fmt.Println(strings.Repeat("=", len(result.Target)))
|
||||
fmt.Printf("Total: %d (%s)\n\n", total, strings.Join(results, ", "))
|
||||
|
||||
if len(result.Vulnerabilities) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
table.SetAutoMergeCells(true)
|
||||
table.SetRowLine(true)
|
||||
table.Render()
|
||||
return
|
||||
}
|
||||
|
||||
func (tw TableWriter) writeVulnerabilities(table *tablewriter.Table, vulns []types.DetectedVulnerability) (int, map[string]int) {
|
||||
header := []string{"Library", "Vulnerability ID", "Severity", "Installed Version", "Fixed Version"}
|
||||
if !tw.Light {
|
||||
header = append(header, "Title")
|
||||
}
|
||||
table.SetHeader(header)
|
||||
severityCount := tw.setVulnerabilityRows(table, vulns)
|
||||
|
||||
return len(vulns), severityCount
|
||||
}
|
||||
|
||||
func (tw TableWriter) setVulnerabilityRows(table *tablewriter.Table, vulns []types.DetectedVulnerability) map[string]int {
|
||||
severityCount := map[string]int{}
|
||||
for _, v := range vulns {
|
||||
severityCount[v.Severity]++
|
||||
|
||||
title := v.Title
|
||||
if title == "" {
|
||||
title = v.Description
|
||||
}
|
||||
splitTitle := strings.Split(title, " ")
|
||||
if len(splitTitle) >= 12 {
|
||||
title = strings.Join(splitTitle[:12], " ") + "..."
|
||||
}
|
||||
|
||||
if len(v.PrimaryURL) > 0 {
|
||||
r := strings.NewReplacer("https://", "", "http://", "")
|
||||
title = fmt.Sprintf("%s -->%s", title, r.Replace(v.PrimaryURL))
|
||||
}
|
||||
|
||||
var row []string
|
||||
if tw.Output == os.Stdout {
|
||||
row = []string{v.PkgName, v.VulnerabilityID, dbTypes.ColorizeSeverity(v.Severity),
|
||||
v.InstalledVersion, v.FixedVersion}
|
||||
} else {
|
||||
row = []string{v.PkgName, v.VulnerabilityID, v.Severity, v.InstalledVersion, v.FixedVersion}
|
||||
}
|
||||
|
||||
if !tw.Light {
|
||||
row = append(row, strings.TrimSpace(title))
|
||||
}
|
||||
table.Append(row)
|
||||
}
|
||||
return severityCount
|
||||
}
|
||||
145
pkg/report/table_test.go
Normal file
145
pkg/report/table_test.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package report_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/report"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
)
|
||||
|
||||
func TestReportWriter_Table(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
results report.Results
|
||||
expectedOutput string
|
||||
light bool
|
||||
}{
|
||||
{
|
||||
name: "happy path full",
|
||||
results: report.Results{
|
||||
{
|
||||
Target: "test",
|
||||
Vulnerabilities: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2020-0001",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "3.4.5",
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "foobar",
|
||||
Description: "baz",
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedOutput: `+---------+------------------+----------+-------------------+---------------+--------------------------------------+
|
||||
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
|
||||
+---------+------------------+----------+-------------------+---------------+--------------------------------------+
|
||||
| foo | CVE-2020-0001 | HIGH | 1.2.3 | 3.4.5 | foobar |
|
||||
| | | | | | -->avd.aquasec.com/nvd/cve-2020-0001 |
|
||||
+---------+------------------+----------+-------------------+---------------+--------------------------------------+
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "happy path light",
|
||||
light: true,
|
||||
results: report.Results{
|
||||
{
|
||||
Target: "test",
|
||||
Vulnerabilities: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2020-0001",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "3.4.5",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "foobar",
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedOutput: `+---------+------------------+----------+-------------------+---------------+
|
||||
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION |
|
||||
+---------+------------------+----------+-------------------+---------------+
|
||||
| foo | CVE-2020-0001 | HIGH | 1.2.3 | 3.4.5 |
|
||||
+---------+------------------+----------+-------------------+---------------+
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "no title for vuln and missing primary link",
|
||||
results: report.Results{
|
||||
{
|
||||
Target: "test",
|
||||
Vulnerabilities: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2020-0001",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "3.4.5",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Description: "foobar",
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedOutput: `+---------+------------------+----------+-------------------+---------------+--------+
|
||||
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
|
||||
+---------+------------------+----------+-------------------+---------------+--------+
|
||||
| foo | CVE-2020-0001 | HIGH | 1.2.3 | 3.4.5 | foobar |
|
||||
+---------+------------------+----------+-------------------+---------------+--------+
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "long title for vuln",
|
||||
results: report.Results{
|
||||
{
|
||||
Target: "test",
|
||||
Vulnerabilities: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2020-1234",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "3.4.5",
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-1234",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "a b c d e f g h i j k l m n o p q r s t u v",
|
||||
Description: "foobar",
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedOutput: `+---------+------------------+----------+-------------------+---------------+--------------------------------------+
|
||||
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
|
||||
+---------+------------------+----------+-------------------+---------------+--------------------------------------+
|
||||
| foo | CVE-2020-1234 | HIGH | 1.2.3 | 3.4.5 | a b c d e f g h i j k l... |
|
||||
| | | | | | -->avd.aquasec.com/nvd/cve-2020-1234 |
|
||||
+---------+------------------+----------+-------------------+---------------+--------------------------------------+
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "no vulns",
|
||||
expectedOutput: ``,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tableWritten := bytes.Buffer{}
|
||||
assert.NoError(t, report.WriteResults("table", &tableWritten, nil, tc.results, "", tc.light), tc.name)
|
||||
assert.Equal(t, tc.expectedOutput, tableWritten.String(), tc.name)
|
||||
})
|
||||
}
|
||||
}
|
||||
121
pkg/report/template.go
Normal file
121
pkg/report/template.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"html"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
|
||||
|
||||
"github.com/Masterminds/sprig"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
// regex to extract file path in case string includes (distro:version)
|
||||
var re = regexp.MustCompile(`(?P<path>.+?)(?:\s*\((?:.*?)\).*?)?$`)
|
||||
|
||||
// TemplateWriter write result in custom format defined by user's template
|
||||
type TemplateWriter struct {
|
||||
Output io.Writer
|
||||
Template *template.Template
|
||||
}
|
||||
|
||||
// NewTemplateWriter is the factory method to return TemplateWriter object
|
||||
func NewTemplateWriter(output io.Writer, outputTemplate string) (*TemplateWriter, error) {
|
||||
if strings.HasPrefix(outputTemplate, "@") {
|
||||
buf, err := ioutil.ReadFile(strings.TrimPrefix(outputTemplate, "@"))
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("error retrieving template from path: %w", err)
|
||||
}
|
||||
outputTemplate = string(buf)
|
||||
}
|
||||
var templateFuncMap template.FuncMap
|
||||
templateFuncMap = sprig.GenericFuncMap()
|
||||
templateFuncMap["escapeXML"] = func(input string) string {
|
||||
escaped := &bytes.Buffer{}
|
||||
if err := xml.EscapeText(escaped, []byte(input)); err != nil {
|
||||
fmt.Printf("error while escapeString to XML: %v", err.Error())
|
||||
return input
|
||||
}
|
||||
return escaped.String()
|
||||
}
|
||||
templateFuncMap["toSarifErrorLevel"] = toSarifErrorLevel
|
||||
templateFuncMap["toSarifRuleName"] = toSarifRuleName
|
||||
templateFuncMap["endWithPeriod"] = func(input string) string {
|
||||
if !strings.HasSuffix(input, ".") {
|
||||
input += "."
|
||||
}
|
||||
return input
|
||||
}
|
||||
templateFuncMap["toLower"] = func(input string) string {
|
||||
return strings.ToLower(input)
|
||||
}
|
||||
templateFuncMap["escapeString"] = func(input string) string {
|
||||
return html.EscapeString(input)
|
||||
}
|
||||
templateFuncMap["toPathUri"] = func(input string) string {
|
||||
var matches = re.FindStringSubmatch(input)
|
||||
if matches != nil {
|
||||
input = matches[re.SubexpIndex("path")]
|
||||
}
|
||||
input = strings.ReplaceAll(input, "\\", "/")
|
||||
return input
|
||||
}
|
||||
templateFuncMap["getEnv"] = func(key string) string {
|
||||
return os.Getenv(key)
|
||||
}
|
||||
templateFuncMap["getCurrentTime"] = func() string {
|
||||
return Now().UTC().Format(time.RFC3339Nano)
|
||||
}
|
||||
tmpl, err := template.New("output template").Funcs(templateFuncMap).Parse(outputTemplate)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("error parsing template: %w", err)
|
||||
}
|
||||
return &TemplateWriter{Output: output, Template: tmpl}, nil
|
||||
}
|
||||
|
||||
// Write writes result
|
||||
func (tw TemplateWriter) Write(results Results) error {
|
||||
err := tw.Template.Execute(tw.Output, results)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to write with template: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func toSarifRuleName(vulnerabilityType string) string {
|
||||
var ruleName string
|
||||
switch vulnerabilityType {
|
||||
case vulnerability.Ubuntu, vulnerability.Alpine, vulnerability.RedHat, vulnerability.RedHatOVAL,
|
||||
vulnerability.Debian, vulnerability.DebianOVAL, vulnerability.Fedora, vulnerability.Amazon,
|
||||
vulnerability.OracleOVAL, vulnerability.SuseCVRF, vulnerability.OpenSuseCVRF, vulnerability.Photon,
|
||||
vulnerability.CentOS:
|
||||
ruleName = "OS Package Vulnerability"
|
||||
case "npm", "yarn", "nuget", "pipenv", "poetry", "bundler", "cargo", "composer":
|
||||
ruleName = "Programming Language Vulnerability"
|
||||
default:
|
||||
ruleName = "Other Vulnerability"
|
||||
}
|
||||
return fmt.Sprintf("%s (%s)", ruleName, strings.Title(vulnerabilityType))
|
||||
}
|
||||
|
||||
func toSarifErrorLevel(severity string) string {
|
||||
switch severity {
|
||||
case "CRITICAL", "HIGH":
|
||||
return "error"
|
||||
case "MEDIUM":
|
||||
return "warning"
|
||||
case "LOW", "UNKNOWN":
|
||||
return "note"
|
||||
default:
|
||||
return "none"
|
||||
}
|
||||
}
|
||||
211
pkg/report/template_test.go
Normal file
211
pkg/report/template_test.go
Normal file
@@ -0,0 +1,211 @@
|
||||
package report_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/report"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
)
|
||||
|
||||
func TestReportWriter_Template(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
detectedVulns []types.DetectedVulnerability
|
||||
template string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
detectedVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0000",
|
||||
PkgName: "foo",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityHigh.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0000",
|
||||
PkgName: "bar",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityHigh.String()},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
PkgName: "baz",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityCritical.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
template: "{{ range . }}{{ range .Vulnerabilities}}{{ println .VulnerabilityID .Severity }}{{ end }}{{ end }}",
|
||||
expected: "CVE-2019-0000 HIGH\nCVE-2019-0000 HIGH\nCVE-2019-0001 CRITICAL\n",
|
||||
},
|
||||
{
|
||||
name: "happy path",
|
||||
detectedVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "123",
|
||||
PkgName: `foo \ test`,
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "3.4.5",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: `gcc: POWER9 "DARN" RNG intrinsic produces repeated output`,
|
||||
Description: `curl version curl \X 7.20.0 to and including curl 7.59.0 contains a CWE-126: Buffer Over-read vulnerability in denial of service that can result in curl can be tricked into reading data beyond the end of a heap based buffer used to store downloaded RTSP content.. This vulnerability appears to have been fixed in curl < 7.20.0 and curl >= 7.60.0.`,
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
template: `<testsuites>
|
||||
{{- range . -}}
|
||||
{{- $failures := len .Vulnerabilities }}
|
||||
<testsuite tests="{{ $failures }}" failures="{{ $failures }}" name="{{ .Target }}" errors="0" skipped="0" time="">
|
||||
{{- if not (eq .Type "") }}
|
||||
<properties>
|
||||
<property name="type" value="{{ .Type }}"></property>
|
||||
</properties>
|
||||
{{- end -}}
|
||||
{{ range .Vulnerabilities }}
|
||||
<testcase classname="{{ .PkgName }}-{{ .InstalledVersion }}" name="[{{ .Vulnerability.Severity }}] {{ .VulnerabilityID }}" time="">
|
||||
<failure message="{{ escapeXML .Title }}" type="description">{{ escapeXML .Description }}</failure>
|
||||
</testcase>
|
||||
{{- end }}
|
||||
</testsuite>
|
||||
{{- end }}
|
||||
</testsuites>`,
|
||||
|
||||
expected: `<testsuites>
|
||||
<testsuite tests="1" failures="1" name="foojunit" errors="0" skipped="0" time="">
|
||||
<properties>
|
||||
<property name="type" value="test"></property>
|
||||
</properties>
|
||||
<testcase classname="foo \ test-1.2.3" name="[HIGH] 123" time="">
|
||||
<failure message="gcc: POWER9 "DARN" RNG intrinsic produces repeated output" type="description">curl version curl \X 7.20.0 to and including curl 7.59.0 contains a CWE-126: Buffer Over-read vulnerability in denial of service that can result in curl can be tricked into reading data beyond the end of a heap based buffer used to store downloaded RTSP content.. This vulnerability appears to have been fixed in curl < 7.20.0 and curl >= 7.60.0.</failure>
|
||||
</testcase>
|
||||
</testsuite>
|
||||
</testsuites>`,
|
||||
},
|
||||
{
|
||||
name: "happy path with/without period description should return with period",
|
||||
detectedVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0000",
|
||||
PkgName: "foo",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Description: "without period",
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0000",
|
||||
PkgName: "bar",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Description: "with period.",
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0000",
|
||||
PkgName: "bar",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Description: `with period and unescaped string curl: Use-after-free when closing 'easy' handle in Curl_close().`,
|
||||
},
|
||||
},
|
||||
},
|
||||
template: `{{ range . }}{{ range .Vulnerabilities}}{{.VulnerabilityID}} {{ endWithPeriod (escapeString .Description) | printf "%q" }}{{ end }}{{ end }}`,
|
||||
expected: `CVE-2019-0000 "without period."CVE-2019-0000 "with period."CVE-2019-0000 "with period and unescaped string curl: Use-after-free when closing 'easy' handle in Curl_close()."`,
|
||||
},
|
||||
{
|
||||
name: "Calculate using sprig",
|
||||
detectedVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0000",
|
||||
PkgName: "foo",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Description: "without period",
|
||||
Severity: dbTypes.SeverityCritical.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0000",
|
||||
PkgName: "bar",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Description: "with period.",
|
||||
Severity: dbTypes.SeverityCritical.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0000",
|
||||
PkgName: "bar",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Description: `with period and unescaped string curl: Use-after-free when closing 'easy' handle in Curl_close().`,
|
||||
Severity: dbTypes.SeverityHigh.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
template: `{{ $high := 0 }}{{ $critical := 0 }}{{ range . }}{{ range .Vulnerabilities}}{{ if eq .Severity "HIGH" }}{{ $high = add $high 1 }}{{ end }}{{ if eq .Severity "CRITICAL" }}{{ $critical = add $critical 1 }}{{ end }}{{ end }}Critical: {{ $critical }}, High: {{ $high }}{{ end }}`,
|
||||
expected: `Critical: 2, High: 1`,
|
||||
},
|
||||
{
|
||||
name: "happy path: env var parsing and getCurrentTime",
|
||||
detectedVulns: []types.DetectedVulnerability{},
|
||||
template: `{{ toLower (getEnv "AWS_ACCOUNT_ID") }} {{ getCurrentTime }}`,
|
||||
expected: `123456789012 2020-08-10T07:28:17.000958601Z`,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
report.Now = func() time.Time {
|
||||
return time.Date(2020, 8, 10, 7, 28, 17, 958601, time.UTC)
|
||||
}
|
||||
os.Setenv("AWS_ACCOUNT_ID", "123456789012")
|
||||
tmplWritten := bytes.Buffer{}
|
||||
inputResults := report.Results{
|
||||
{
|
||||
Target: "foojunit",
|
||||
Type: "test",
|
||||
Vulnerabilities: tc.detectedVulns,
|
||||
},
|
||||
}
|
||||
|
||||
assert.NoError(t, report.WriteResults("template", &tmplWritten, nil, inputResults, tc.template, false))
|
||||
assert.Equal(t, tc.expected, tmplWritten.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReportWriter_Template_SARIF(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
target string
|
||||
detectedVulns []types.DetectedVulnerability
|
||||
want string
|
||||
}{
|
||||
//TODO: refactor tests
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
templateFile := "../../contrib/sarif.tpl"
|
||||
got := bytes.Buffer{}
|
||||
|
||||
template, err := ioutil.ReadFile(templateFile)
|
||||
require.NoError(t, err, tc.name)
|
||||
|
||||
inputResults := report.Results{
|
||||
report.Result{
|
||||
Target: tc.target,
|
||||
Type: "footype",
|
||||
Vulnerabilities: tc.detectedVulns,
|
||||
},
|
||||
}
|
||||
assert.NoError(t, report.WriteResults("template", &got, nil, inputResults, string(template), false), tc.name)
|
||||
assert.JSONEq(t, tc.want, got.String(), tc.name)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,19 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"html"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/sprig"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer/library"
|
||||
ftypes "github.com/aquasecurity/fanal/types"
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/utils"
|
||||
)
|
||||
|
||||
// Now returns the current time
|
||||
var Now = time.Now
|
||||
|
||||
// regex to extract file path in case string includes (distro:version)
|
||||
var re = regexp.MustCompile(`(?P<path>.+?)(?:\s*\((?:.*?)\).*?)?$`)
|
||||
|
||||
// Results to hold list of Result
|
||||
type Results []Result
|
||||
|
||||
@@ -40,7 +22,17 @@ type Result struct {
|
||||
Target string `json:"Target"`
|
||||
Type string `json:"Type,omitempty"`
|
||||
Packages []ftypes.Package `json:"Packages,omitempty"`
|
||||
Vulnerabilities []types.DetectedVulnerability `json:"Vulnerabilities"`
|
||||
Vulnerabilities []types.DetectedVulnerability `json:"Vulnerabilities,omitempty"`
|
||||
}
|
||||
|
||||
// Failed returns whether the result includes any vulnerabilities
|
||||
func (results Results) Failed() bool {
|
||||
for _, r := range results {
|
||||
if len(r.Vulnerabilities) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// WriteResults writes the result to output, format as passed in argument
|
||||
@@ -70,211 +62,3 @@ func WriteResults(format string, output io.Writer, severities []dbTypes.Severity
|
||||
type Writer interface {
|
||||
Write(Results) error
|
||||
}
|
||||
|
||||
// TableWriter implements Writer and output in tabular form
|
||||
type TableWriter struct {
|
||||
Severities []dbTypes.Severity
|
||||
Output io.Writer
|
||||
Light bool
|
||||
}
|
||||
|
||||
// Write writes the result on standard output
|
||||
func (tw TableWriter) Write(results Results) error {
|
||||
for _, result := range results {
|
||||
// Skip zero vulnerabilities on Java archives (JAR/WAR/EAR)
|
||||
if result.Type == library.Jar && len(result.Vulnerabilities) == 0 {
|
||||
continue
|
||||
}
|
||||
tw.write(result)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tw TableWriter) write(result Result) {
|
||||
table := tablewriter.NewWriter(tw.Output)
|
||||
header := []string{"Library", "Vulnerability ID", "Severity", "Installed Version", "Fixed Version"}
|
||||
if !tw.Light {
|
||||
header = append(header, "Title")
|
||||
}
|
||||
table.SetHeader(header)
|
||||
severityCount := tw.setRows(table, result.Vulnerabilities)
|
||||
|
||||
var results []string
|
||||
|
||||
var severities []string
|
||||
for _, sev := range tw.Severities {
|
||||
severities = append(severities, sev.String())
|
||||
}
|
||||
|
||||
for _, severity := range dbTypes.SeverityNames {
|
||||
if !utils.StringInSlice(severity, severities) {
|
||||
continue
|
||||
}
|
||||
r := fmt.Sprintf("%s: %d", severity, severityCount[severity])
|
||||
results = append(results, r)
|
||||
}
|
||||
|
||||
fmt.Printf("\n%s\n", result.Target)
|
||||
fmt.Println(strings.Repeat("=", len(result.Target)))
|
||||
fmt.Printf("Total: %d (%s)\n\n", len(result.Vulnerabilities), strings.Join(results, ", "))
|
||||
|
||||
if len(result.Vulnerabilities) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
table.SetAutoMergeCells(true)
|
||||
table.SetRowLine(true)
|
||||
table.Render()
|
||||
return
|
||||
}
|
||||
|
||||
func (tw TableWriter) setRows(table *tablewriter.Table, vulns []types.DetectedVulnerability) map[string]int {
|
||||
severityCount := map[string]int{}
|
||||
for _, v := range vulns {
|
||||
severityCount[v.Severity]++
|
||||
|
||||
title := v.Title
|
||||
if title == "" {
|
||||
title = v.Description
|
||||
}
|
||||
splitTitle := strings.Split(title, " ")
|
||||
if len(splitTitle) >= 12 {
|
||||
title = strings.Join(splitTitle[:12], " ") + "..."
|
||||
}
|
||||
|
||||
if len(v.PrimaryURL) > 0 {
|
||||
r := strings.NewReplacer("https://", "", "http://", "")
|
||||
title = fmt.Sprintf("%s -->%s", title, r.Replace(v.PrimaryURL))
|
||||
}
|
||||
|
||||
var row []string
|
||||
if tw.Output == os.Stdout {
|
||||
row = []string{v.PkgName, v.VulnerabilityID, dbTypes.ColorizeSeverity(v.Severity),
|
||||
v.InstalledVersion, v.FixedVersion}
|
||||
} else {
|
||||
row = []string{v.PkgName, v.VulnerabilityID, v.Severity, v.InstalledVersion, v.FixedVersion}
|
||||
}
|
||||
|
||||
if !tw.Light {
|
||||
row = append(row, strings.TrimSpace(title))
|
||||
}
|
||||
table.Append(row)
|
||||
}
|
||||
return severityCount
|
||||
}
|
||||
|
||||
// JSONWriter implements result Writer
|
||||
type JSONWriter struct {
|
||||
Output io.Writer
|
||||
}
|
||||
|
||||
// Write writes the results in JSON format
|
||||
func (jw JSONWriter) Write(results Results) error {
|
||||
output, err := json.MarshalIndent(results, "", " ")
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to marshal json: %w", err)
|
||||
}
|
||||
|
||||
if _, err = fmt.Fprint(jw.Output, string(output)); err != nil {
|
||||
return xerrors.Errorf("failed to write json: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TemplateWriter write result in custom format defined by user's template
|
||||
type TemplateWriter struct {
|
||||
Output io.Writer
|
||||
Template *template.Template
|
||||
}
|
||||
|
||||
// NewTemplateWriter is the factory method to return TemplateWriter object
|
||||
func NewTemplateWriter(output io.Writer, outputTemplate string) (*TemplateWriter, error) {
|
||||
if strings.HasPrefix(outputTemplate, "@") {
|
||||
buf, err := ioutil.ReadFile(strings.TrimPrefix(outputTemplate, "@"))
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("error retrieving template from path: %w", err)
|
||||
}
|
||||
outputTemplate = string(buf)
|
||||
}
|
||||
var templateFuncMap template.FuncMap
|
||||
templateFuncMap = sprig.GenericFuncMap()
|
||||
templateFuncMap["escapeXML"] = func(input string) string {
|
||||
escaped := &bytes.Buffer{}
|
||||
if err := xml.EscapeText(escaped, []byte(input)); err != nil {
|
||||
fmt.Printf("error while escapeString to XML: %v", err.Error())
|
||||
return input
|
||||
}
|
||||
return escaped.String()
|
||||
}
|
||||
templateFuncMap["toSarifErrorLevel"] = toSarifErrorLevel
|
||||
templateFuncMap["toSarifRuleName"] = toSarifRuleName
|
||||
templateFuncMap["endWithPeriod"] = func(input string) string {
|
||||
if !strings.HasSuffix(input, ".") {
|
||||
input += "."
|
||||
}
|
||||
return input
|
||||
}
|
||||
templateFuncMap["toLower"] = func(input string) string {
|
||||
return strings.ToLower(input)
|
||||
}
|
||||
templateFuncMap["escapeString"] = func(input string) string {
|
||||
return html.EscapeString(input)
|
||||
}
|
||||
templateFuncMap["toPathUri"] = func(input string) string {
|
||||
var matches = re.FindStringSubmatch(input)
|
||||
if matches != nil {
|
||||
input = matches[re.SubexpIndex("path")]
|
||||
}
|
||||
input = strings.ReplaceAll(input, "\\", "/")
|
||||
return input
|
||||
}
|
||||
templateFuncMap["getEnv"] = func(key string) string {
|
||||
return os.Getenv(key)
|
||||
}
|
||||
templateFuncMap["getCurrentTime"] = func() string {
|
||||
return Now().UTC().Format(time.RFC3339Nano)
|
||||
}
|
||||
tmpl, err := template.New("output template").Funcs(templateFuncMap).Parse(outputTemplate)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("error parsing template: %w", err)
|
||||
}
|
||||
return &TemplateWriter{Output: output, Template: tmpl}, nil
|
||||
}
|
||||
|
||||
// Write writes result
|
||||
func (tw TemplateWriter) Write(results Results) error {
|
||||
err := tw.Template.Execute(tw.Output, results)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to write with template: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func toSarifRuleName(vulnerabilityType string) string {
|
||||
var ruleName string
|
||||
switch vulnerabilityType {
|
||||
case vulnerability.Ubuntu, vulnerability.Alpine, vulnerability.RedHat, vulnerability.RedHatOVAL,
|
||||
vulnerability.Debian, vulnerability.DebianOVAL, vulnerability.Fedora, vulnerability.Amazon,
|
||||
vulnerability.OracleOVAL, vulnerability.SuseCVRF, vulnerability.OpenSuseCVRF, vulnerability.Photon,
|
||||
vulnerability.CentOS:
|
||||
ruleName = "OS Package Vulnerability"
|
||||
case "npm", "yarn", "nuget", "pipenv", "poetry", "bundler", "cargo", "composer":
|
||||
ruleName = "Programming Language Vulnerability"
|
||||
default:
|
||||
ruleName = "Other Vulnerability"
|
||||
}
|
||||
return fmt.Sprintf("%s (%s)", ruleName, strings.Title(vulnerabilityType))
|
||||
}
|
||||
|
||||
func toSarifErrorLevel(severity string) string {
|
||||
switch severity {
|
||||
case "CRITICAL", "HIGH":
|
||||
return "error"
|
||||
case "MEDIUM":
|
||||
return "warning"
|
||||
case "LOW", "UNKNOWN":
|
||||
return "note"
|
||||
default:
|
||||
return "none"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,570 +1,50 @@
|
||||
package report_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/report"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestReportWriter_Table(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
detectedVulns []types.DetectedVulnerability
|
||||
expectedOutput string
|
||||
light bool
|
||||
func TestResults_Failed(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
results report.Results
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "happy path full",
|
||||
detectedVulns: []types.DetectedVulnerability{
|
||||
name: "no vulnerabilities and misconfigurations",
|
||||
results: report.Results{
|
||||
{
|
||||
VulnerabilityID: "CVE-2020-0001",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "3.4.5",
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "foobar",
|
||||
Description: "baz",
|
||||
Severity: "HIGH",
|
||||
},
|
||||
Target: "test",
|
||||
Type: "test",
|
||||
},
|
||||
},
|
||||
expectedOutput: `+---------+------------------+----------+-------------------+---------------+--------------------------------------+
|
||||
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
|
||||
+---------+------------------+----------+-------------------+---------------+--------------------------------------+
|
||||
| foo | CVE-2020-0001 | HIGH | 1.2.3 | 3.4.5 | foobar |
|
||||
| | | | | | -->avd.aquasec.com/nvd/cve-2020-0001 |
|
||||
+---------+------------------+----------+-------------------+---------------+--------------------------------------+
|
||||
`,
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "happy path light",
|
||||
light: true,
|
||||
detectedVulns: []types.DetectedVulnerability{
|
||||
name: "vulnerabilities found",
|
||||
results: report.Results{
|
||||
{
|
||||
VulnerabilityID: "123",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "3.4.5",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "foobar",
|
||||
Description: "baz",
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedOutput: `+---------+------------------+----------+-------------------+---------------+
|
||||
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION |
|
||||
+---------+------------------+----------+-------------------+---------------+
|
||||
| foo | 123 | HIGH | 1.2.3 | 3.4.5 |
|
||||
+---------+------------------+----------+-------------------+---------------+
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "no title for vuln and missing primary link",
|
||||
detectedVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "123",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "3.4.5",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Description: "foobar",
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedOutput: `+---------+------------------+----------+-------------------+---------------+--------+
|
||||
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
|
||||
+---------+------------------+----------+-------------------+---------------+--------+
|
||||
| foo | 123 | HIGH | 1.2.3 | 3.4.5 | foobar |
|
||||
+---------+------------------+----------+-------------------+---------------+--------+
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "long title for vuln",
|
||||
detectedVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2020-1234",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "3.4.5",
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "a b c d e f g h i j k l m n o p q r s t u v",
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedOutput: `+---------+------------------+----------+-------------------+---------------+--------------------------------------+
|
||||
| LIBRARY | VULNERABILITY ID | SEVERITY | INSTALLED VERSION | FIXED VERSION | TITLE |
|
||||
+---------+------------------+----------+-------------------+---------------+--------------------------------------+
|
||||
| foo | CVE-2020-1234 | HIGH | 1.2.3 | 3.4.5 | a b c d e f g h i j k l... |
|
||||
| | | | | | -->avd.aquasec.com/nvd/cve-2020-0001 |
|
||||
+---------+------------------+----------+-------------------+---------------+--------------------------------------+
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "no vulns",
|
||||
detectedVulns: []types.DetectedVulnerability{},
|
||||
expectedOutput: ``,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
inputResults := report.Results{
|
||||
{
|
||||
Target: "foo",
|
||||
Vulnerabilities: tc.detectedVulns,
|
||||
},
|
||||
}
|
||||
tableWritten := bytes.Buffer{}
|
||||
assert.NoError(t, report.WriteResults("table", &tableWritten, nil, inputResults, "", tc.light), tc.name)
|
||||
assert.Equal(t, tc.expectedOutput, tableWritten.String(), tc.name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReportWriter_JSON(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
detectedVulns []types.DetectedVulnerability
|
||||
expectedJSON report.Results
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
detectedVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2020-0001",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "3.4.5",
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "foobar",
|
||||
Description: "baz",
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedJSON: report.Results{
|
||||
report.Result{
|
||||
Target: "foojson",
|
||||
Target: "test",
|
||||
Type: "test",
|
||||
Vulnerabilities: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2020-0001",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "3.4.5",
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "foobar",
|
||||
Description: "baz",
|
||||
Severity: "HIGH",
|
||||
},
|
||||
VulnerabilityID: "CVE-2021-0001",
|
||||
PkgName: "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
||||
jw := report.JSONWriter{}
|
||||
jsonWritten := bytes.Buffer{}
|
||||
jw.Output = &jsonWritten
|
||||
|
||||
inputResults := report.Results{
|
||||
{
|
||||
Target: "foojson",
|
||||
Vulnerabilities: tc.detectedVulns,
|
||||
},
|
||||
}
|
||||
|
||||
assert.NoError(t, report.WriteResults("json", &jsonWritten, nil, inputResults, "", false), tc.name)
|
||||
|
||||
writtenResults := report.Results{}
|
||||
errJson := json.Unmarshal([]byte(jsonWritten.String()), &writtenResults)
|
||||
assert.NoError(t, errJson, "invalid json written", tc.name)
|
||||
|
||||
assert.Equal(t, tc.expectedJSON, writtenResults, tc.name)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestReportWriter_Template(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
detectedVulns []types.DetectedVulnerability
|
||||
template string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
detectedVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0000",
|
||||
PkgName: "foo",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityHigh.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0000",
|
||||
PkgName: "bar",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityHigh.String()},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
PkgName: "baz",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityCritical.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
template: "{{ range . }}{{ range .Vulnerabilities}}{{ println .VulnerabilityID .Severity }}{{ end }}{{ end }}",
|
||||
expected: "CVE-2019-0000 HIGH\nCVE-2019-0000 HIGH\nCVE-2019-0001 CRITICAL\n",
|
||||
},
|
||||
{
|
||||
name: "happy path",
|
||||
detectedVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "123",
|
||||
PkgName: `foo \ test`,
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "3.4.5",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: `gcc: POWER9 "DARN" RNG intrinsic produces repeated output`,
|
||||
Description: `curl version curl \X 7.20.0 to and including curl 7.59.0 contains a CWE-126: Buffer Over-read vulnerability in denial of service that can result in curl can be tricked into reading data beyond the end of a heap based buffer used to store downloaded RTSP content.. This vulnerability appears to have been fixed in curl < 7.20.0 and curl >= 7.60.0.`,
|
||||
Severity: "HIGH",
|
||||
},
|
||||
},
|
||||
},
|
||||
template: `<testsuites>
|
||||
{{- range . -}}
|
||||
{{- $failures := len .Vulnerabilities }}
|
||||
<testsuite tests="{{ $failures }}" failures="{{ $failures }}" name="{{ .Target }}" errors="0" skipped="0" time="">
|
||||
{{- if not (eq .Type "") }}
|
||||
<properties>
|
||||
<property name="type" value="{{ .Type }}"></property>
|
||||
</properties>
|
||||
{{- end -}}
|
||||
{{ range .Vulnerabilities }}
|
||||
<testcase classname="{{ .PkgName }}-{{ .InstalledVersion }}" name="[{{ .Vulnerability.Severity }}] {{ .VulnerabilityID }}" time="">
|
||||
<failure message="{{ escapeXML .Title }}" type="description">{{ escapeXML .Description }}</failure>
|
||||
</testcase>
|
||||
{{- end }}
|
||||
</testsuite>
|
||||
{{- end }}
|
||||
</testsuites>`,
|
||||
|
||||
expected: `<testsuites>
|
||||
<testsuite tests="1" failures="1" name="foojunit" errors="0" skipped="0" time="">
|
||||
<properties>
|
||||
<property name="type" value="test"></property>
|
||||
</properties>
|
||||
<testcase classname="foo \ test-1.2.3" name="[HIGH] 123" time="">
|
||||
<failure message="gcc: POWER9 "DARN" RNG intrinsic produces repeated output" type="description">curl version curl \X 7.20.0 to and including curl 7.59.0 contains a CWE-126: Buffer Over-read vulnerability in denial of service that can result in curl can be tricked into reading data beyond the end of a heap based buffer used to store downloaded RTSP content.. This vulnerability appears to have been fixed in curl < 7.20.0 and curl >= 7.60.0.</failure>
|
||||
</testcase>
|
||||
</testsuite>
|
||||
</testsuites>`,
|
||||
},
|
||||
{
|
||||
name: "happy path with/without period description should return with period",
|
||||
detectedVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0000",
|
||||
PkgName: "foo",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Description: "without period",
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0000",
|
||||
PkgName: "bar",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Description: "with period.",
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0000",
|
||||
PkgName: "bar",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Description: `with period and unescaped string curl: Use-after-free when closing 'easy' handle in Curl_close().`,
|
||||
},
|
||||
},
|
||||
},
|
||||
template: `{{ range . }}{{ range .Vulnerabilities}}{{.VulnerabilityID}} {{ endWithPeriod (escapeString .Description) | printf "%q" }}{{ end }}{{ end }}`,
|
||||
expected: `CVE-2019-0000 "without period."CVE-2019-0000 "with period."CVE-2019-0000 "with period and unescaped string curl: Use-after-free when closing 'easy' handle in Curl_close()."`,
|
||||
},
|
||||
{
|
||||
name: "Calculate using sprig",
|
||||
detectedVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0000",
|
||||
PkgName: "foo",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Description: "without period",
|
||||
Severity: dbTypes.SeverityCritical.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0000",
|
||||
PkgName: "bar",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Description: "with period.",
|
||||
Severity: dbTypes.SeverityCritical.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0000",
|
||||
PkgName: "bar",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Description: `with period and unescaped string curl: Use-after-free when closing 'easy' handle in Curl_close().`,
|
||||
Severity: dbTypes.SeverityHigh.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
template: `{{ $high := 0 }}{{ $critical := 0 }}{{ range . }}{{ range .Vulnerabilities}}{{ if eq .Severity "HIGH" }}{{ $high = add $high 1 }}{{ end }}{{ if eq .Severity "CRITICAL" }}{{ $critical = add $critical 1 }}{{ end }}{{ end }}Critical: {{ $critical }}, High: {{ $high }}{{ end }}`,
|
||||
expected: `Critical: 2, High: 1`,
|
||||
},
|
||||
{
|
||||
name: "happy path: env var parsing and getCurrentTime",
|
||||
detectedVulns: []types.DetectedVulnerability{},
|
||||
template: `{{ toLower (getEnv "AWS_ACCOUNT_ID") }} {{ getCurrentTime }}`,
|
||||
expected: `123456789012 2020-08-10T07:28:17.000958601Z`,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
report.Now = func() time.Time {
|
||||
return time.Date(2020, 8, 10, 7, 28, 17, 958601, time.UTC)
|
||||
}
|
||||
os.Setenv("AWS_ACCOUNT_ID", "123456789012")
|
||||
tmplWritten := bytes.Buffer{}
|
||||
inputResults := report.Results{
|
||||
{
|
||||
Target: "foojunit",
|
||||
Type: "test",
|
||||
Vulnerabilities: tc.detectedVulns,
|
||||
},
|
||||
}
|
||||
|
||||
assert.NoError(t, report.WriteResults("template", &tmplWritten, nil, inputResults, tc.template, false))
|
||||
assert.Equal(t, tc.expected, tmplWritten.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestReportWriter_Template_SARIF(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
target string
|
||||
detectedVulns []types.DetectedVulnerability
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "no primary url",
|
||||
target: "foo/target/alpine-310.tar.gz (alpine 3.10.2)",
|
||||
detectedVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-1234-5678",
|
||||
PkgName: "foopackage",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "4.5.6",
|
||||
SeveritySource: "NVD",
|
||||
PrimaryURL: "",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "foovuln",
|
||||
Description: "foodesc",
|
||||
Severity: "CRITICAL",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: `{
|
||||
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
|
||||
"version": "2.1.0",
|
||||
"runs": [
|
||||
{
|
||||
"tool": {
|
||||
"driver": {
|
||||
"name": "Trivy",
|
||||
"informationUri": "https://github.com/aquasecurity/trivy",
|
||||
"fullName": "Trivy Vulnerability Scanner",
|
||||
"version": "0.15.0",
|
||||
"rules": [
|
||||
{
|
||||
"id": "CVE-1234-5678/foopackage",
|
||||
"name": "Other Vulnerability (Footype)",
|
||||
"shortDescription": {
|
||||
"text": "CVE-1234-5678 Package: foopackage"
|
||||
},
|
||||
"fullDescription": {
|
||||
"text": "foovuln."
|
||||
},
|
||||
"defaultConfiguration": {
|
||||
"level": "error"
|
||||
},
|
||||
"help": {
|
||||
"text": "Vulnerability CVE-1234-5678\nSeverity: CRITICAL\nPackage: foopackage\nInstalled Version: 1.2.3\nFixed Version: 4.5.6\nLink: [CVE-1234-5678]()",
|
||||
"markdown": "**Vulnerability CVE-1234-5678**\n| Severity | Package | Installed Version | Fixed Version | Link |\n| --- | --- | --- | --- | --- |\n|CRITICAL|foopackage|1.2.3|4.5.6|[CVE-1234-5678]()|\n"
|
||||
},
|
||||
"properties": {
|
||||
"tags": [
|
||||
"vulnerability",
|
||||
"CRITICAL",
|
||||
"foopackage"
|
||||
],
|
||||
"precision": "very-high"
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"ruleId": "CVE-1234-5678/foopackage",
|
||||
"ruleIndex": 0,
|
||||
"level": "error",
|
||||
"message": {
|
||||
"text": "foodesc."
|
||||
},
|
||||
"locations": [{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "foo/target/alpine-310.tar.gz",
|
||||
"uriBaseId": "ROOTPATH"
|
||||
}
|
||||
}
|
||||
}]
|
||||
}],
|
||||
"columnKind": "utf16CodeUnits",
|
||||
"originalUriBaseIds": {
|
||||
"ROOTPATH": {
|
||||
"uri": "/"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
{
|
||||
name: "with primary url",
|
||||
target: "rust-app\\Cargo.lock",
|
||||
detectedVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-1234-5678",
|
||||
PkgName: "foopackage",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "4.5.6",
|
||||
SeveritySource: "NVD",
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-1234-5678",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "foovuln",
|
||||
Description: "foodesc",
|
||||
Severity: "CRITICAL",
|
||||
},
|
||||
},
|
||||
},
|
||||
want: `{
|
||||
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
|
||||
"version": "2.1.0",
|
||||
"runs": [
|
||||
{
|
||||
"tool": {
|
||||
"driver": {
|
||||
"name": "Trivy",
|
||||
"informationUri": "https://github.com/aquasecurity/trivy",
|
||||
"fullName": "Trivy Vulnerability Scanner",
|
||||
"version": "0.15.0",
|
||||
"rules": [
|
||||
{
|
||||
"id": "CVE-1234-5678/foopackage",
|
||||
"name": "Other Vulnerability (Footype)",
|
||||
"shortDescription": {
|
||||
"text": "CVE-1234-5678 Package: foopackage"
|
||||
},
|
||||
"fullDescription": {
|
||||
"text": "foovuln."
|
||||
},
|
||||
"defaultConfiguration": {
|
||||
"level": "error"
|
||||
},
|
||||
"helpUri": "https://avd.aquasec.com/nvd/cve-1234-5678",
|
||||
"help": {
|
||||
"text": "Vulnerability CVE-1234-5678\nSeverity: CRITICAL\nPackage: foopackage\nInstalled Version: 1.2.3\nFixed Version: 4.5.6\nLink: [CVE-1234-5678](https://avd.aquasec.com/nvd/cve-1234-5678)",
|
||||
"markdown": "**Vulnerability CVE-1234-5678**\n| Severity | Package | Installed Version | Fixed Version | Link |\n| --- | --- | --- | --- | --- |\n|CRITICAL|foopackage|1.2.3|4.5.6|[CVE-1234-5678](https://avd.aquasec.com/nvd/cve-1234-5678)|\n"
|
||||
},
|
||||
"properties": {
|
||||
"tags": [
|
||||
"vulnerability",
|
||||
"CRITICAL",
|
||||
"foopackage"
|
||||
],
|
||||
"precision": "very-high"
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
"results": [
|
||||
{
|
||||
"ruleId": "CVE-1234-5678/foopackage",
|
||||
"ruleIndex": 0,
|
||||
"level": "error",
|
||||
"message": {
|
||||
"text": "foodesc."
|
||||
},
|
||||
"locations": [{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "rust-app/Cargo.lock",
|
||||
"uriBaseId": "ROOTPATH"
|
||||
}
|
||||
}
|
||||
}]
|
||||
}],
|
||||
"columnKind": "utf16CodeUnits",
|
||||
"originalUriBaseIds": {
|
||||
"ROOTPATH": {
|
||||
"uri": "/"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}`,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
templateFile := "../../contrib/sarif.tpl"
|
||||
got := bytes.Buffer{}
|
||||
|
||||
template, err := ioutil.ReadFile(templateFile)
|
||||
require.NoError(t, err, tc.name)
|
||||
|
||||
inputResults := report.Results{
|
||||
report.Result{
|
||||
Target: tc.target,
|
||||
Type: "footype",
|
||||
Vulnerabilities: tc.detectedVulns,
|
||||
},
|
||||
}
|
||||
assert.NoError(t, report.WriteResults("template", &got, nil, inputResults, string(template), false), tc.name)
|
||||
assert.JSONEq(t, tc.want, got.String(), tc.name)
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := tt.results.Failed()
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,8 @@ func (s Scanner) Scan(target string, imageID string, layerIDs []string, options
|
||||
ArtifactId: imageID,
|
||||
BlobIds: layerIDs,
|
||||
Options: &rpc.ScanOptions{
|
||||
VulnType: options.VulnType,
|
||||
VulnType: options.VulnType,
|
||||
SecurityChecks: options.SecurityChecks,
|
||||
},
|
||||
})
|
||||
return err
|
||||
|
||||
@@ -117,10 +117,7 @@ func ConvertToRPCVulns(vulns []types.DetectedVulnerability) []*common.Vulnerabil
|
||||
Description: vuln.Description,
|
||||
Severity: common.Severity(severity),
|
||||
References: vuln.References,
|
||||
Layer: &common.Layer{
|
||||
Digest: vuln.Layer.Digest,
|
||||
DiffId: vuln.Layer.DiffID,
|
||||
},
|
||||
Layer: ConvertToRPCLayer(vuln.Layer),
|
||||
Cvss: cvssMap,
|
||||
SeveritySource: vuln.SeveritySource,
|
||||
CweIds: vuln.CweIDs,
|
||||
@@ -132,65 +129,83 @@ func ConvertToRPCVulns(vulns []types.DetectedVulnerability) []*common.Vulnerabil
|
||||
return rpcVulns
|
||||
}
|
||||
|
||||
// ConvertToRPCLayer returns common.Layer
|
||||
func ConvertToRPCLayer(layer ftypes.Layer) *common.Layer {
|
||||
return &common.Layer{
|
||||
Digest: layer.Digest,
|
||||
DiffId: layer.DiffID,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertFromRPCResults converts scanner.Result to report.Result
|
||||
func ConvertFromRPCResults(rpcResults []*scanner.Result) []report.Result {
|
||||
var results []report.Result
|
||||
for _, result := range rpcResults {
|
||||
var vulns []types.DetectedVulnerability
|
||||
for _, vuln := range result.Vulnerabilities {
|
||||
severity := dbTypes.Severity(vuln.Severity)
|
||||
cvssMap := make(dbTypes.VendorCVSS) // This is needed because protobuf generates a map[string]*CVSS type
|
||||
for vendor, vendorSeverity := range vuln.Cvss {
|
||||
cvssMap[vendor] = dbTypes.CVSS{
|
||||
V2Vector: vendorSeverity.V2Vector,
|
||||
V3Vector: vendorSeverity.V3Vector,
|
||||
V2Score: vendorSeverity.V2Score,
|
||||
V3Score: vendorSeverity.V3Score,
|
||||
}
|
||||
}
|
||||
|
||||
var lastModifiedDate, publishedDate *time.Time
|
||||
if vuln.LastModifiedDate != nil {
|
||||
t, _ := ptypes.Timestamp(vuln.LastModifiedDate) // nolint: errcheck
|
||||
lastModifiedDate = &t
|
||||
}
|
||||
if vuln.PublishedDate != nil {
|
||||
t, _ := ptypes.Timestamp(vuln.PublishedDate) // nolint: errcheck
|
||||
publishedDate = &t
|
||||
}
|
||||
|
||||
vulns = append(vulns, types.DetectedVulnerability{
|
||||
VulnerabilityID: vuln.VulnerabilityId,
|
||||
PkgName: vuln.PkgName,
|
||||
InstalledVersion: vuln.InstalledVersion,
|
||||
FixedVersion: vuln.FixedVersion,
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: vuln.Title,
|
||||
Description: vuln.Description,
|
||||
Severity: severity.String(),
|
||||
CVSS: cvssMap,
|
||||
References: vuln.References,
|
||||
CweIDs: vuln.CweIds,
|
||||
LastModifiedDate: lastModifiedDate,
|
||||
PublishedDate: publishedDate,
|
||||
},
|
||||
Layer: ftypes.Layer{
|
||||
Digest: vuln.Layer.Digest,
|
||||
DiffID: vuln.Layer.DiffId,
|
||||
},
|
||||
SeveritySource: vuln.SeveritySource,
|
||||
PrimaryURL: vuln.PrimaryUrl,
|
||||
})
|
||||
}
|
||||
results = append(results, report.Result{
|
||||
Target: result.Target,
|
||||
Vulnerabilities: vulns,
|
||||
Vulnerabilities: ConvertFromRPCVulns(result.Vulnerabilities),
|
||||
Type: result.Type,
|
||||
})
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// ConvertFromRPCVulns converts []*common.Vulnerability to []types.DetectedVulnerability
|
||||
func ConvertFromRPCVulns(rpcVulns []*common.Vulnerability) []types.DetectedVulnerability {
|
||||
var vulns []types.DetectedVulnerability
|
||||
for _, vuln := range rpcVulns {
|
||||
severity := dbTypes.Severity(vuln.Severity)
|
||||
cvssMap := make(dbTypes.VendorCVSS) // This is needed because protobuf generates a map[string]*CVSS type
|
||||
for vendor, vendorSeverity := range vuln.Cvss {
|
||||
cvssMap[vendor] = dbTypes.CVSS{
|
||||
V2Vector: vendorSeverity.V2Vector,
|
||||
V3Vector: vendorSeverity.V3Vector,
|
||||
V2Score: vendorSeverity.V2Score,
|
||||
V3Score: vendorSeverity.V3Score,
|
||||
}
|
||||
}
|
||||
|
||||
var lastModifiedDate, publishedDate *time.Time
|
||||
if vuln.LastModifiedDate != nil {
|
||||
t, _ := ptypes.Timestamp(vuln.LastModifiedDate) // nolint: errcheck
|
||||
lastModifiedDate = &t
|
||||
}
|
||||
if vuln.PublishedDate != nil {
|
||||
t, _ := ptypes.Timestamp(vuln.PublishedDate) // nolint: errcheck
|
||||
publishedDate = &t
|
||||
}
|
||||
|
||||
vulns = append(vulns, types.DetectedVulnerability{
|
||||
VulnerabilityID: vuln.VulnerabilityId,
|
||||
PkgName: vuln.PkgName,
|
||||
InstalledVersion: vuln.InstalledVersion,
|
||||
FixedVersion: vuln.FixedVersion,
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: vuln.Title,
|
||||
Description: vuln.Description,
|
||||
Severity: severity.String(),
|
||||
CVSS: cvssMap,
|
||||
References: vuln.References,
|
||||
CweIDs: vuln.CweIds,
|
||||
LastModifiedDate: lastModifiedDate,
|
||||
PublishedDate: publishedDate,
|
||||
},
|
||||
Layer: ConvertFromRPCLayer(vuln.Layer),
|
||||
SeveritySource: vuln.SeveritySource,
|
||||
PrimaryURL: vuln.PrimaryUrl,
|
||||
})
|
||||
}
|
||||
return vulns
|
||||
}
|
||||
|
||||
// ConvertFromRPCLayer converts *common.Layer to fanal.Layer
|
||||
func ConvertFromRPCLayer(rpcLayer *common.Layer) ftypes.Layer {
|
||||
return ftypes.Layer{
|
||||
Digest: rpcLayer.Digest,
|
||||
DiffID: rpcLayer.DiffId,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertFromRPCOS converts common.OS to fanal.OS
|
||||
func ConvertFromRPCOS(rpcOS *common.OS) *ftypes.OS {
|
||||
if rpcOS == nil {
|
||||
@@ -286,9 +301,9 @@ func ConvertToRPCArtifactInfo(imageID string, imageInfo ftypes.ArtifactInfo) *ca
|
||||
}
|
||||
|
||||
// ConvertToRPCBlobInfo returns PutBlobRequest
|
||||
func ConvertToRPCBlobInfo(diffID string, layerInfo ftypes.BlobInfo) *cache.PutBlobRequest {
|
||||
func ConvertToRPCBlobInfo(diffID string, blobInfo ftypes.BlobInfo) *cache.PutBlobRequest {
|
||||
var packageInfos []*common.PackageInfo
|
||||
for _, pkgInfo := range layerInfo.PackageInfos {
|
||||
for _, pkgInfo := range blobInfo.PackageInfos {
|
||||
packageInfos = append(packageInfos, &common.PackageInfo{
|
||||
FilePath: pkgInfo.FilePath,
|
||||
Packages: ConvertToRPCPkgs(pkgInfo.Packages),
|
||||
@@ -296,7 +311,7 @@ func ConvertToRPCBlobInfo(diffID string, layerInfo ftypes.BlobInfo) *cache.PutBl
|
||||
}
|
||||
|
||||
var applications []*common.Application
|
||||
for _, app := range layerInfo.Applications {
|
||||
for _, app := range blobInfo.Applications {
|
||||
var libs []*common.Library
|
||||
for _, lib := range app.Libraries {
|
||||
libs = append(libs, &common.Library{
|
||||
@@ -315,13 +330,13 @@ func ConvertToRPCBlobInfo(diffID string, layerInfo ftypes.BlobInfo) *cache.PutBl
|
||||
DiffId: diffID,
|
||||
BlobInfo: &cache.BlobInfo{
|
||||
SchemaVersion: ftypes.BlobJSONSchemaVersion,
|
||||
Digest: layerInfo.Digest,
|
||||
DiffId: layerInfo.DiffID,
|
||||
Os: ConvertToRPCOS(layerInfo.OS),
|
||||
Digest: blobInfo.Digest,
|
||||
DiffId: blobInfo.DiffID,
|
||||
Os: ConvertToRPCOS(blobInfo.OS),
|
||||
PackageInfos: packageInfos,
|
||||
Applications: applications,
|
||||
OpaqueDirs: layerInfo.OpaqueDirs,
|
||||
WhiteoutFiles: layerInfo.WhiteoutFiles,
|
||||
OpaqueDirs: blobInfo.OpaqueDirs,
|
||||
WhiteoutFiles: blobInfo.WhiteoutFiles,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -346,8 +361,8 @@ func ConvertToRPCScanResponse(results report.Results, os *ftypes.OS, eosl bool)
|
||||
for _, result := range results {
|
||||
rpcResults = append(rpcResults, &scanner.Result{
|
||||
Target: result.Target,
|
||||
Vulnerabilities: ConvertToRPCVulns(result.Vulnerabilities),
|
||||
Type: result.Type,
|
||||
Vulnerabilities: ConvertToRPCVulns(result.Vulnerabilities),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -28,24 +28,27 @@ var ScanSuperSet = wire.NewSet(
|
||||
// ScanServer implements the scanner
|
||||
type ScanServer struct {
|
||||
localScanner scanner.Driver
|
||||
vulnClient vulnerability.Operation
|
||||
resultClient vulnerability.Client
|
||||
}
|
||||
|
||||
// NewScanServer is the factory method for scanner
|
||||
func NewScanServer(s scanner.Driver, vulnClient vulnerability.Operation) *ScanServer {
|
||||
return &ScanServer{localScanner: s, vulnClient: vulnClient}
|
||||
func NewScanServer(s scanner.Driver, vulnClient vulnerability.Client) *ScanServer {
|
||||
return &ScanServer{localScanner: s, resultClient: vulnClient}
|
||||
}
|
||||
|
||||
// Scan scans and return response
|
||||
func (s *ScanServer) Scan(_ context.Context, in *rpcScanner.ScanRequest) (*rpcScanner.ScanResponse, error) {
|
||||
options := types.ScanOptions{VulnType: in.Options.VulnType}
|
||||
options := types.ScanOptions{
|
||||
VulnType: in.Options.VulnType,
|
||||
SecurityChecks: in.Options.SecurityChecks,
|
||||
}
|
||||
results, os, eosl, err := s.localScanner.Scan(in.Target, in.ArtifactId, in.BlobIds, options)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed scan, %s: %w", in.Target, err)
|
||||
}
|
||||
|
||||
for i := range results {
|
||||
s.vulnClient.FillInfo(results[i].Vulnerabilities, results[i].Type)
|
||||
s.resultClient.FillInfo(results[i].Vulnerabilities, results[i].Type)
|
||||
}
|
||||
return rpc.ConvertToRPCScanResponse(results, os, eosl), nil
|
||||
}
|
||||
|
||||
@@ -16,8 +16,10 @@ import (
|
||||
"github.com/aquasecurity/fanal/cache"
|
||||
ftypes "github.com/aquasecurity/fanal/types"
|
||||
deptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
|
||||
"github.com/aquasecurity/trivy-db/pkg/db"
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy-db/pkg/utils"
|
||||
"github.com/aquasecurity/trivy/pkg/dbtest"
|
||||
"github.com/aquasecurity/trivy/pkg/report"
|
||||
"github.com/aquasecurity/trivy/pkg/scanner"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
@@ -37,15 +39,16 @@ func TestScanServer_Scan(t *testing.T) {
|
||||
in *rpcScanner.ScanRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
scanExpectation scanner.DriverScanExpectation
|
||||
fillInfoExpectation vulnerability.OperationFillInfoExpectation
|
||||
want *rpcScanner.ScanResponse
|
||||
wantErr string
|
||||
name string
|
||||
fixtures []string
|
||||
args args
|
||||
scanExpectation scanner.DriverScanExpectation
|
||||
want *rpcScanner.ScanResponse
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
name: "happy path",
|
||||
fixtures: []string{"testdata/fixtures/vulnerability.yaml"},
|
||||
args: args{
|
||||
in: &rpcScanner.ScanRequest{
|
||||
Target: "alpine:3.11",
|
||||
@@ -70,7 +73,6 @@ func TestScanServer_Scan(t *testing.T) {
|
||||
PkgName: "musl",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "1.2.4",
|
||||
SeveritySource: "nvd",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
LastModifiedDate: utils.MustTimeParse("2020-01-01T01:01:00Z"),
|
||||
PublishedDate: utils.MustTimeParse("2001-01-01T01:01:00Z"),
|
||||
@@ -86,24 +88,6 @@ func TestScanServer_Scan(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
fillInfoExpectation: vulnerability.OperationFillInfoExpectation{
|
||||
Args: vulnerability.OperationFillInfoArgs{
|
||||
Vulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
PkgName: "musl",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "1.2.4",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
LastModifiedDate: utils.MustTimeParse("2020-01-01T01:01:00Z"),
|
||||
PublishedDate: utils.MustTimeParse("2001-01-01T01:01:00Z"),
|
||||
},
|
||||
SeveritySource: "nvd",
|
||||
},
|
||||
},
|
||||
ReportType: "alpine",
|
||||
},
|
||||
},
|
||||
want: &rpcScanner.ScanResponse{
|
||||
Os: &common.OS{
|
||||
Family: "alpine",
|
||||
@@ -119,9 +103,14 @@ func TestScanServer_Scan(t *testing.T) {
|
||||
PkgName: "musl",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "1.2.4",
|
||||
Severity: common.Severity_MEDIUM,
|
||||
SeveritySource: "nvd",
|
||||
Layer: &common.Layer{},
|
||||
Cvss: make(map[string]*common.CVSS),
|
||||
Cvss: map[string]*common.CVSS{},
|
||||
PrimaryUrl: "https://avd.aquasec.com/nvd/cve-2019-0001",
|
||||
Title: "dos",
|
||||
Description: "dos vulnerability",
|
||||
References: []string{"http://example.com"},
|
||||
LastModifiedDate: ×tamp.Timestamp{
|
||||
Seconds: 1577840460,
|
||||
},
|
||||
@@ -161,13 +150,13 @@ func TestScanServer_Scan(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dbtest.InitDB(t, tt.fixtures)
|
||||
defer db.Close()
|
||||
|
||||
mockDriver := new(scanner.MockDriver)
|
||||
mockDriver.ApplyScanExpectation(tt.scanExpectation)
|
||||
|
||||
mockVulnClient := new(vulnerability.MockOperation)
|
||||
mockVulnClient.ApplyFillInfoExpectation(tt.fillInfoExpectation)
|
||||
|
||||
s := NewScanServer(mockDriver, mockVulnClient)
|
||||
s := NewScanServer(mockDriver, vulnerability.NewClient(db.Config{}))
|
||||
got, err := s.Scan(context.Background(), tt.args.in)
|
||||
if tt.wantErr != "" {
|
||||
require.NotNil(t, err, tt.name)
|
||||
|
||||
13
pkg/rpc/server/testdata/fixtures/vulnerability.yaml
vendored
Normal file
13
pkg/rpc/server/testdata/fixtures/vulnerability.yaml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
- bucket: vulnerability
|
||||
pairs:
|
||||
- key: CVE-2019-0001
|
||||
value:
|
||||
Title: dos
|
||||
Description: dos vulnerability
|
||||
Severity: MEDIUM
|
||||
VendorSeverity:
|
||||
nvd: 2
|
||||
References:
|
||||
- http://example.com
|
||||
LastModifiedDate: "2020-01-01T01:01:00Z"
|
||||
PublishedDate: "2001-01-01T01:01:00Z"
|
||||
@@ -13,27 +13,7 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/command/apk"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/bundler"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/cargo"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/composer"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/gobinary"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/jar"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/npm"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/nuget"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/pipenv"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/poetry"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/library/yarn"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/os/alpine"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/os/amazonlinux"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/os/debian"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/os/photon"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/os/redhatbase"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/os/suse"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/os/ubuntu"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/pkg/apk"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/pkg/dpkg"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/pkg/rpm"
|
||||
_ "github.com/aquasecurity/fanal/analyzer/all"
|
||||
"github.com/aquasecurity/fanal/applier"
|
||||
ftypes "github.com/aquasecurity/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/detector/library"
|
||||
@@ -80,7 +60,7 @@ func (s Scanner) Scan(target, versionedArtifactID string, versionedBlobIDs []str
|
||||
artifactDetail, err := s.applier.ApplyLayers(versionedArtifactID, versionedBlobIDs)
|
||||
switch {
|
||||
case errors.Is(err, analyzer.ErrUnknownOS):
|
||||
log.Logger.Warn("OS is not detected and vulnerabilities in OS packages are not detected.")
|
||||
log.Logger.Debug("OS is not detected and vulnerabilities in OS packages are not detected.")
|
||||
case errors.Is(err, analyzer.ErrNoPkgsDetected):
|
||||
log.Logger.Warn("No OS package is detected. Make sure you haven't deleted any files that contain information about the installed packages.")
|
||||
log.Logger.Warn(`e.g. files under "/lib/apk/db/", "/var/lib/dpkg/" and "/var/lib/rpm"`)
|
||||
@@ -91,29 +71,53 @@ func (s Scanner) Scan(target, versionedArtifactID string, versionedBlobIDs []str
|
||||
var eosl bool
|
||||
var results report.Results
|
||||
|
||||
if utils.StringInSlice("os", options.VulnType) && artifactDetail.OS != nil {
|
||||
var result *report.Result
|
||||
result, eosl, err = s.scanOSPkgs(target, artifactDetail, options)
|
||||
// Scan OS packages and programming language dependencies
|
||||
if utils.StringInSlice(types.SecurityCheckVulnerability, options.SecurityChecks) {
|
||||
var vulnResults report.Results
|
||||
vulnResults, eosl, err = s.checkVulnerabilities(target, artifactDetail, options)
|
||||
if err != nil {
|
||||
return nil, nil, false, xerrors.Errorf("unable to scan OS packages: %w", err)
|
||||
} else if result != nil {
|
||||
results = append(results, *result)
|
||||
return nil, nil, false, xerrors.Errorf("failed to detect vulnerabilities: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if utils.StringInSlice("library", options.VulnType) {
|
||||
libResults, err := s.scanLibrary(artifactDetail.Applications, options)
|
||||
if err != nil {
|
||||
return nil, nil, false, xerrors.Errorf("failed to scan application libraries: %w", err)
|
||||
}
|
||||
results = append(results, libResults...)
|
||||
results = append(results, vulnResults...)
|
||||
}
|
||||
|
||||
return results, artifactDetail.OS, eosl, nil
|
||||
}
|
||||
|
||||
func (s Scanner) checkVulnerabilities(target string, detail ftypes.ArtifactDetail, options types.ScanOptions) (
|
||||
report.Results, bool, error) {
|
||||
var eosl bool
|
||||
var results report.Results
|
||||
|
||||
if utils.StringInSlice(types.VulnTypeOS, options.VulnType) {
|
||||
result, detectedEosl, err := s.scanOSPkgs(target, detail, options)
|
||||
if err != nil {
|
||||
return nil, false, xerrors.Errorf("unable to scan OS packages: %w", err)
|
||||
} else if result != nil {
|
||||
results = append(results, *result)
|
||||
}
|
||||
eosl = detectedEosl
|
||||
}
|
||||
|
||||
if utils.StringInSlice(types.VulnTypeLibrary, options.VulnType) {
|
||||
libResults, err := s.scanLibrary(detail.Applications, options)
|
||||
if err != nil {
|
||||
return nil, false, xerrors.Errorf("failed to scan application libraries: %w", err)
|
||||
}
|
||||
results = append(results, libResults...)
|
||||
}
|
||||
|
||||
return results, eosl, nil
|
||||
}
|
||||
|
||||
func (s Scanner) scanOSPkgs(target string, detail ftypes.ArtifactDetail, options types.ScanOptions) (
|
||||
*report.Result, bool, error) {
|
||||
if detail.OS == nil {
|
||||
log.Logger.Infof("Detected OS: unknown")
|
||||
return nil, false, nil
|
||||
}
|
||||
log.Logger.Infof("Detected OS: %s", detail.OS.Family)
|
||||
|
||||
pkgs := detail.Packages
|
||||
if options.ScanRemovedPackages {
|
||||
pkgs = mergePkgs(pkgs, detail.HistoryPackages)
|
||||
@@ -157,8 +161,8 @@ func (s Scanner) detectVulnsInOSPkgs(target, osFamily, osName string, pkgs []fty
|
||||
}
|
||||
|
||||
func (s Scanner) scanLibrary(apps []ftypes.Application, options types.ScanOptions) (report.Results, error) {
|
||||
log.Logger.Infof("Number of PL dependency files: %d", len(apps))
|
||||
if len(apps) == 0 {
|
||||
log.Logger.Info("Trivy skips scanning programming language libraries because no supported file was detected")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -212,6 +216,7 @@ func (s Scanner) scanLibrary(apps []ftypes.Application, options types.ScanOption
|
||||
}
|
||||
|
||||
func skipped(filePath string, skipFiles, skipDirs []string) bool {
|
||||
filePath = strings.TrimLeft(filepath.Clean(filePath), string(os.PathSeparator))
|
||||
for _, skipFile := range skipFiles {
|
||||
skipFile = strings.TrimLeft(filepath.Clean(skipFile), string(os.PathSeparator))
|
||||
if filePath == skipFile {
|
||||
|
||||
@@ -4,19 +4,15 @@ import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer/library"
|
||||
|
||||
"github.com/aquasecurity/trivy-db/pkg/db"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/dbtest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/aquasecurity/fanal/analyzer"
|
||||
ftypes "github.com/aquasecurity/fanal/types"
|
||||
dtypes "github.com/aquasecurity/go-dep-parser/pkg/types"
|
||||
"github.com/aquasecurity/trivy-db/pkg/db"
|
||||
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
|
||||
"github.com/aquasecurity/trivy/pkg/dbtest"
|
||||
ospkgDetector "github.com/aquasecurity/trivy/pkg/detector/ospkg"
|
||||
"github.com/aquasecurity/trivy/pkg/report"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
@@ -44,7 +40,10 @@ func TestScanner_Scan(t *testing.T) {
|
||||
args: args{
|
||||
target: "alpine:latest",
|
||||
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
|
||||
options: types.ScanOptions{VulnType: []string{"os", "library"}},
|
||||
options: types.ScanOptions{
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
},
|
||||
},
|
||||
fixtures: []string{"testdata/fixtures/happy.yaml"},
|
||||
applyLayersExpectation: ApplierApplyLayersExpectation{
|
||||
@@ -68,7 +67,7 @@ func TestScanner_Scan(t *testing.T) {
|
||||
},
|
||||
Applications: []ftypes.Application{
|
||||
{
|
||||
Type: library.Bundler,
|
||||
Type: ftypes.Bundler,
|
||||
FilePath: "/app/Gemfile.lock",
|
||||
Libraries: []ftypes.LibraryInfo{
|
||||
{
|
||||
@@ -156,7 +155,11 @@ func TestScanner_Scan(t *testing.T) {
|
||||
args: args{
|
||||
target: "alpine:latest",
|
||||
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
|
||||
options: types.ScanOptions{VulnType: []string{"os", "library"}, ListAllPackages: true},
|
||||
options: types.ScanOptions{
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
ListAllPackages: true,
|
||||
},
|
||||
},
|
||||
fixtures: []string{"testdata/fixtures/happy.yaml"},
|
||||
applyLayersExpectation: ApplierApplyLayersExpectation{
|
||||
@@ -307,7 +310,10 @@ func TestScanner_Scan(t *testing.T) {
|
||||
args: args{
|
||||
target: "alpine:latest",
|
||||
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
|
||||
options: types.ScanOptions{VulnType: []string{"os", "library"}},
|
||||
options: types.ScanOptions{
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
},
|
||||
},
|
||||
fixtures: []string{"testdata/fixtures/happy.yaml"},
|
||||
applyLayersExpectation: ApplierApplyLayersExpectation{
|
||||
@@ -358,7 +364,10 @@ func TestScanner_Scan(t *testing.T) {
|
||||
args: args{
|
||||
target: "alpine:latest",
|
||||
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
|
||||
options: types.ScanOptions{VulnType: []string{"os", "library"}},
|
||||
options: types.ScanOptions{
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
},
|
||||
},
|
||||
fixtures: []string{"testdata/fixtures/happy.yaml"},
|
||||
applyLayersExpectation: ApplierApplyLayersExpectation{
|
||||
@@ -431,7 +440,10 @@ func TestScanner_Scan(t *testing.T) {
|
||||
args: args{
|
||||
target: "fedora:27",
|
||||
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
|
||||
options: types.ScanOptions{VulnType: []string{"os", "library"}},
|
||||
options: types.ScanOptions{
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
},
|
||||
},
|
||||
fixtures: []string{"testdata/fixtures/happy.yaml"},
|
||||
applyLayersExpectation: ApplierApplyLayersExpectation{
|
||||
@@ -499,7 +511,10 @@ func TestScanner_Scan(t *testing.T) {
|
||||
args: args{
|
||||
target: "busybox:latest",
|
||||
layerIDs: []string{"sha256:a6d503001157aedc826853f9b67f26d35966221b158bff03849868ae4a821116"},
|
||||
options: types.ScanOptions{VulnType: []string{"os", "library"}},
|
||||
options: types.ScanOptions{
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
},
|
||||
},
|
||||
fixtures: []string{"testdata/fixtures/happy.yaml"},
|
||||
applyLayersExpectation: ApplierApplyLayersExpectation{
|
||||
@@ -521,7 +536,10 @@ func TestScanner_Scan(t *testing.T) {
|
||||
args: args{
|
||||
target: "alpine:latest",
|
||||
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
|
||||
options: types.ScanOptions{VulnType: []string{"library"}},
|
||||
options: types.ScanOptions{
|
||||
VulnType: []string{types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
},
|
||||
},
|
||||
fixtures: []string{"testdata/fixtures/happy.yaml"},
|
||||
applyLayersExpectation: ApplierApplyLayersExpectation{
|
||||
@@ -609,8 +627,9 @@ func TestScanner_Scan(t *testing.T) {
|
||||
target: "alpine:latest",
|
||||
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
|
||||
options: types.ScanOptions{
|
||||
VulnType: []string{"library"},
|
||||
SkipDirs: []string{"/usr/lib/ruby/gems"},
|
||||
VulnType: []string{types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
SkipDirs: []string{"/usr/lib/ruby/gems", "/app/k8s"},
|
||||
},
|
||||
},
|
||||
fixtures: []string{"testdata/fixtures/happy.yaml"},
|
||||
@@ -683,7 +702,10 @@ func TestScanner_Scan(t *testing.T) {
|
||||
args: args{
|
||||
target: "alpine:latest",
|
||||
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
|
||||
options: types.ScanOptions{VulnType: []string{"os", "library"}},
|
||||
options: types.ScanOptions{
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
},
|
||||
},
|
||||
fixtures: []string{"testdata/fixtures/happy.yaml"},
|
||||
applyLayersExpectation: ApplierApplyLayersExpectation{
|
||||
@@ -701,7 +723,10 @@ func TestScanner_Scan(t *testing.T) {
|
||||
args: args{
|
||||
target: "alpine:latest",
|
||||
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
|
||||
options: types.ScanOptions{VulnType: []string{"os", "library"}},
|
||||
options: types.ScanOptions{
|
||||
VulnType: []string{types.VulnTypeOS, types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
},
|
||||
},
|
||||
fixtures: []string{"testdata/fixtures/happy.yaml"},
|
||||
applyLayersExpectation: ApplierApplyLayersExpectation{
|
||||
@@ -753,7 +778,10 @@ func TestScanner_Scan(t *testing.T) {
|
||||
args: args{
|
||||
target: "alpine:latest",
|
||||
layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
|
||||
options: types.ScanOptions{VulnType: []string{"library"}},
|
||||
options: types.ScanOptions{
|
||||
VulnType: []string{types.VulnTypeLibrary},
|
||||
SecurityChecks: []string{types.SecurityCheckVulnerability},
|
||||
},
|
||||
},
|
||||
fixtures: []string{"testdata/fixtures/sad.yaml"},
|
||||
applyLayersExpectation: ApplierApplyLayersExpectation{
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
aimage "github.com/aquasecurity/fanal/artifact/image"
|
||||
flocal "github.com/aquasecurity/fanal/artifact/local"
|
||||
"github.com/aquasecurity/fanal/artifact/remote"
|
||||
"github.com/aquasecurity/fanal/cache"
|
||||
"github.com/aquasecurity/fanal/image"
|
||||
ftypes "github.com/aquasecurity/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
@@ -83,7 +82,8 @@ type Scanner struct {
|
||||
|
||||
// Driver defines operations of scanner
|
||||
type Driver interface {
|
||||
Scan(target string, imageID string, layerIDs []string, options types.ScanOptions) (results report.Results, osFound *ftypes.OS, eols bool, err error)
|
||||
Scan(target string, imageID string, layerIDs []string, options types.ScanOptions) (
|
||||
results report.Results, osFound *ftypes.OS, eols bool, err error)
|
||||
}
|
||||
|
||||
// NewScanner is the factory method of Scanner
|
||||
@@ -98,14 +98,6 @@ func (s Scanner) ScanArtifact(ctx context.Context, options types.ScanOptions) (r
|
||||
return nil, xerrors.Errorf("failed analysis: %w", err)
|
||||
}
|
||||
|
||||
// Debug information
|
||||
var blobIDs []string
|
||||
for _, b := range artifactInfo.BlobIDs {
|
||||
blobIDs = append(blobIDs, cache.TrimVersionSuffix(b))
|
||||
}
|
||||
log.Logger.Debugf("Artifact ID: %s", cache.TrimVersionSuffix(artifactInfo.ID))
|
||||
log.Logger.Debugf("Blob IDs: %v", blobIDs)
|
||||
|
||||
results, osFound, eosl, err := s.driver.Scan(artifactInfo.Name, artifactInfo.ID, artifactInfo.BlobIDs, options)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("scan failed: %w", err)
|
||||
|
||||
@@ -3,6 +3,7 @@ package types
|
||||
// ScanOptions holds the attributes for scanning vulnerabilities
|
||||
type ScanOptions struct {
|
||||
VulnType []string
|
||||
SecurityChecks []string
|
||||
ScanRemovedPackages bool
|
||||
ListAllPackages bool
|
||||
SkipFiles []string
|
||||
|
||||
47
pkg/types/target.go
Normal file
47
pkg/types/target.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package types
|
||||
|
||||
import "github.com/aquasecurity/trivy/pkg/utils"
|
||||
|
||||
// VulnType represents vulnerability type
|
||||
type VulnType = string
|
||||
|
||||
// SecurityCheck represents the type of security check
|
||||
type SecurityCheck = string
|
||||
|
||||
const (
|
||||
// VulnTypeUnknown is a vulnerability type of unknown
|
||||
VulnTypeUnknown = VulnType("unknown")
|
||||
|
||||
// VulnTypeOS is a vulnerability type of OS packages
|
||||
VulnTypeOS = VulnType("os")
|
||||
|
||||
// VulnTypeLibrary is a vulnerability type of programming language dependencies
|
||||
VulnTypeLibrary = VulnType("library")
|
||||
|
||||
// SecurityCheckUnknown is a security check of unknown
|
||||
SecurityCheckUnknown = SecurityCheck("unknown")
|
||||
|
||||
// SecurityCheckVulnerability is a security check of vulnerabilities
|
||||
SecurityCheckVulnerability = SecurityCheck("vuln")
|
||||
)
|
||||
|
||||
var (
|
||||
vulnTypes = []string{VulnTypeOS, VulnTypeLibrary}
|
||||
securityChecks = []string{SecurityCheckVulnerability}
|
||||
)
|
||||
|
||||
// NewVulnType returns an instance of VulnType
|
||||
func NewVulnType(s string) VulnType {
|
||||
if utils.StringInSlice(s, vulnTypes) {
|
||||
return s
|
||||
}
|
||||
return VulnTypeUnknown
|
||||
}
|
||||
|
||||
// NewSecurityCheck returns an instance of SecurityCheck
|
||||
func NewSecurityCheck(s string) SecurityCheck {
|
||||
if utils.StringInSlice(s, securityChecks) {
|
||||
return s
|
||||
}
|
||||
return SecurityCheckUnknown
|
||||
}
|
||||
@@ -28,6 +28,8 @@ func (v BySeverity) Len() int { return len(v) }
|
||||
func (v BySeverity) Less(i, j int) bool {
|
||||
if v[i].PkgName != v[j].PkgName {
|
||||
return v[i].PkgName < v[j].PkgName
|
||||
} else if v[i].InstalledVersion != v[j].InstalledVersion {
|
||||
return v[i].InstalledVersion < v[j].InstalledVersion
|
||||
}
|
||||
ret := types.CompareSeverityString(
|
||||
v[j].Severity, v[i].Severity,
|
||||
|
||||
@@ -28,7 +28,7 @@ func CacheDir() string {
|
||||
return cacheDir
|
||||
}
|
||||
|
||||
// SetCacheDir sets the tricy cacheDir
|
||||
// SetCacheDir sets the trivy cacheDir
|
||||
func SetCacheDir(dir string) {
|
||||
cacheDir = dir
|
||||
}
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||
|
||||
package vulnerability
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
pkgtypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
|
||||
types "github.com/aquasecurity/trivy/pkg/types"
|
||||
)
|
||||
|
||||
// MockOperation is an autogenerated mock type for the Operation type
|
||||
type MockOperation struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type OperationFillInfoArgs struct {
|
||||
Vulns []types.DetectedVulnerability
|
||||
VulnsAnything bool
|
||||
ReportType string
|
||||
ReportTypeAnything bool
|
||||
}
|
||||
|
||||
type OperationFillInfoExpectation struct {
|
||||
Args OperationFillInfoArgs
|
||||
}
|
||||
|
||||
func (_m *MockOperation) ApplyFillInfoExpectation(e OperationFillInfoExpectation) {
|
||||
var args []interface{}
|
||||
if e.Args.VulnsAnything {
|
||||
args = append(args, mock.Anything)
|
||||
} else {
|
||||
args = append(args, e.Args.Vulns)
|
||||
}
|
||||
if e.Args.ReportTypeAnything {
|
||||
args = append(args, mock.Anything)
|
||||
} else {
|
||||
args = append(args, e.Args.ReportType)
|
||||
}
|
||||
_m.On("FillInfo", args...)
|
||||
}
|
||||
|
||||
func (_m *MockOperation) ApplyFillInfoExpectations(expectations []OperationFillInfoExpectation) {
|
||||
for _, e := range expectations {
|
||||
_m.ApplyFillInfoExpectation(e)
|
||||
}
|
||||
}
|
||||
|
||||
// FillInfo provides a mock function with given fields: vulns, reportType
|
||||
func (_m *MockOperation) FillInfo(vulns []types.DetectedVulnerability, reportType string) {
|
||||
_m.Called(vulns, reportType)
|
||||
}
|
||||
|
||||
type OperationFilterArgs struct {
|
||||
Ctx context.Context
|
||||
CtxAnything bool
|
||||
Vulns []types.DetectedVulnerability
|
||||
VulnsAnything bool
|
||||
Severities []pkgtypes.Severity
|
||||
SeveritiesAnything bool
|
||||
IgnoreUnfixed bool
|
||||
IgnoreUnfixedAnything bool
|
||||
IgnoreFile string
|
||||
IgnoreFileAnything bool
|
||||
Policy string
|
||||
PolicyAnything bool
|
||||
}
|
||||
|
||||
type OperationFilterReturns struct {
|
||||
_a0 []types.DetectedVulnerability
|
||||
_a1 error
|
||||
}
|
||||
|
||||
type OperationFilterExpectation struct {
|
||||
Args OperationFilterArgs
|
||||
Returns OperationFilterReturns
|
||||
}
|
||||
|
||||
func (_m *MockOperation) ApplyFilterExpectation(e OperationFilterExpectation) {
|
||||
var args []interface{}
|
||||
if e.Args.CtxAnything {
|
||||
args = append(args, mock.Anything)
|
||||
} else {
|
||||
args = append(args, e.Args.Ctx)
|
||||
}
|
||||
if e.Args.VulnsAnything {
|
||||
args = append(args, mock.Anything)
|
||||
} else {
|
||||
args = append(args, e.Args.Vulns)
|
||||
}
|
||||
if e.Args.SeveritiesAnything {
|
||||
args = append(args, mock.Anything)
|
||||
} else {
|
||||
args = append(args, e.Args.Severities)
|
||||
}
|
||||
if e.Args.IgnoreUnfixedAnything {
|
||||
args = append(args, mock.Anything)
|
||||
} else {
|
||||
args = append(args, e.Args.IgnoreUnfixed)
|
||||
}
|
||||
if e.Args.IgnoreFileAnything {
|
||||
args = append(args, mock.Anything)
|
||||
} else {
|
||||
args = append(args, e.Args.IgnoreFile)
|
||||
}
|
||||
if e.Args.PolicyAnything {
|
||||
args = append(args, mock.Anything)
|
||||
} else {
|
||||
args = append(args, e.Args.Policy)
|
||||
}
|
||||
_m.On("Filter", args...).Return(e.Returns._a0, e.Returns._a1)
|
||||
}
|
||||
|
||||
func (_m *MockOperation) ApplyFilterExpectations(expectations []OperationFilterExpectation) {
|
||||
for _, e := range expectations {
|
||||
_m.ApplyFilterExpectation(e)
|
||||
}
|
||||
}
|
||||
|
||||
// Filter provides a mock function with given fields: ctx, vulns, severities, ignoreUnfixed, ignoreFile, policy
|
||||
func (_m *MockOperation) Filter(ctx context.Context, vulns []types.DetectedVulnerability, severities []pkgtypes.Severity, ignoreUnfixed bool, ignoreFile string, policy string) ([]types.DetectedVulnerability, error) {
|
||||
ret := _m.Called(ctx, vulns, severities, ignoreUnfixed, ignoreFile, policy)
|
||||
|
||||
var r0 []types.DetectedVulnerability
|
||||
if rf, ok := ret.Get(0).(func(context.Context, []types.DetectedVulnerability, []pkgtypes.Severity, bool, string, string) []types.DetectedVulnerability); ok {
|
||||
r0 = rf(ctx, vulns, severities, ignoreUnfixed, ignoreFile, policy)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]types.DetectedVulnerability)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(context.Context, []types.DetectedVulnerability, []pkgtypes.Severity, bool, string, string) error); ok {
|
||||
r1 = rf(ctx, vulns, severities, ignoreUnfixed, ignoreFile, policy)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
5
pkg/vulnerability/testdata/.trivyignore
vendored
5
pkg/vulnerability/testdata/.trivyignore
vendored
@@ -1,3 +1,6 @@
|
||||
# test
|
||||
# vulnerabilities
|
||||
CVE-2019-0001
|
||||
CVE-2019-0002
|
||||
|
||||
# misconfigurations
|
||||
ID100
|
||||
59
pkg/vulnerability/testdata/fixtures/full.yaml
vendored
Normal file
59
pkg/vulnerability/testdata/fixtures/full.yaml
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
- bucket: vulnerability
|
||||
pairs:
|
||||
- key: CVE-2019-0001
|
||||
value:
|
||||
Title: dos
|
||||
Description: dos vulnerability
|
||||
Severity: MEDIUM
|
||||
References:
|
||||
- http://example.com
|
||||
LastModifiedDate: "2020-01-01T01:01:00Z"
|
||||
PublishedDate: "2001-01-01T01:01:00Z"
|
||||
- key: CVE-2019-0002
|
||||
value:
|
||||
Title: dos
|
||||
Description: dos vulnerability
|
||||
Severity: UNKNOWN
|
||||
VendorSeverity:
|
||||
nvd: 1
|
||||
References:
|
||||
- http://example.com
|
||||
LastModifiedDate: "2020-01-01T01:01:00Z"
|
||||
PublishedDate: "2001-01-01T01:01:00Z"
|
||||
- key: CVE-2019-0003
|
||||
value:
|
||||
Title: dos
|
||||
Description: dos vulnerability
|
||||
References:
|
||||
- http://example.com
|
||||
- key: CVE-2019-0004
|
||||
value:
|
||||
Title: dos
|
||||
Description: dos vulnerability
|
||||
Severity: MEDIUM
|
||||
VendorSeverity:
|
||||
redhat: 1
|
||||
CVSS:
|
||||
nvd:
|
||||
V2Vector: AV:N/AC:L/Au:N/C:P/I:P/A:P
|
||||
V2Score: 4.5
|
||||
V3Vector: CVSS:3.0/PR:N/UI:N/S:U/C:H/I:H/A:H
|
||||
V3Score: 5.6
|
||||
redhat:
|
||||
V2Vector: AV:N/AC:M/Au:N/C:N/I:P/A:N
|
||||
V2Score: 7.8
|
||||
V3Vector: CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
|
||||
V3Score: 9.8
|
||||
References:
|
||||
- http://example.com
|
||||
CweIDs:
|
||||
- CWE-311
|
||||
- key: CVE-2019-0005
|
||||
value:
|
||||
Title: COVID-19
|
||||
Description: a nasty virus vulnerability for humans
|
||||
Severity: MEDIUM
|
||||
VendorSeverity:
|
||||
python-safety-db: 4
|
||||
References:
|
||||
- "https://www.who.int/emergencies/diseases/novel-coronavirus-2019"
|
||||
11
pkg/vulnerability/testdata/fixtures/light.yaml
vendored
Normal file
11
pkg/vulnerability/testdata/fixtures/light.yaml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
- bucket: vulnerability
|
||||
pairs:
|
||||
- key: CVE-2020-0001
|
||||
value:
|
||||
Title: dos
|
||||
Severity: MEDIUM
|
||||
VendorSeverity:
|
||||
ubuntu: 1
|
||||
- key: CVE-2020-0002
|
||||
value:
|
||||
Title: dos
|
||||
6
pkg/vulnerability/testdata/fixtures/sad.yaml
vendored
Normal file
6
pkg/vulnerability/testdata/fixtures/sad.yaml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
- bucket: vulnerability
|
||||
pairs:
|
||||
- key: CVE-2019-0001
|
||||
value:
|
||||
Title:
|
||||
- aaa
|
||||
@@ -3,6 +3,7 @@ package vulnerability
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sort"
|
||||
@@ -148,25 +149,8 @@ func (c Client) getPrimaryURL(vulnID string, refs []string, source string) strin
|
||||
func (c Client) Filter(ctx context.Context, vulns []types.DetectedVulnerability, severities []dbTypes.Severity,
|
||||
ignoreUnfixed bool, ignoreFile string, policyFile string) ([]types.DetectedVulnerability, error) {
|
||||
ignoredIDs := getIgnoredIDs(ignoreFile)
|
||||
var vulnerabilities []types.DetectedVulnerability
|
||||
for _, vuln := range vulns {
|
||||
if vuln.Severity == "" {
|
||||
vuln.Severity = dbTypes.SeverityUnknown.String()
|
||||
}
|
||||
// Filter vulnerabilities by severity
|
||||
for _, s := range severities {
|
||||
if s.String() == vuln.Severity {
|
||||
// Ignore unfixed vulnerabilities
|
||||
if ignoreUnfixed && vuln.FixedVersion == "" {
|
||||
continue
|
||||
} else if utils.StringInSlice(vuln.VulnerabilityID, ignoredIDs) {
|
||||
continue
|
||||
}
|
||||
vulnerabilities = append(vulnerabilities, vuln)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vulnerabilities := filterVulnerabilities(vulns, severities, ignoredIDs, ignoreUnfixed)
|
||||
|
||||
if policyFile != "" {
|
||||
var err error
|
||||
@@ -179,6 +163,49 @@ func (c Client) Filter(ctx context.Context, vulns []types.DetectedVulnerability,
|
||||
return vulnerabilities, nil
|
||||
}
|
||||
|
||||
func filterVulnerabilities(vulns []types.DetectedVulnerability, severities []dbTypes.Severity, ignoredIDs []string,
|
||||
ignoreUnfixed bool) []types.DetectedVulnerability {
|
||||
uniqVulns := make(map[string]types.DetectedVulnerability)
|
||||
for _, vuln := range vulns {
|
||||
if vuln.Severity == "" {
|
||||
vuln.Severity = dbTypes.SeverityUnknown.String()
|
||||
}
|
||||
// Filter vulnerabilities by severity
|
||||
for _, s := range severities {
|
||||
if s.String() != vuln.Severity {
|
||||
continue
|
||||
}
|
||||
|
||||
// Ignore unfixed vulnerabilities
|
||||
if ignoreUnfixed && vuln.FixedVersion == "" {
|
||||
continue
|
||||
} else if utils.StringInSlice(vuln.VulnerabilityID, ignoredIDs) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if there is a duplicate vulnerability
|
||||
key := fmt.Sprintf("%s/%s/%s", vuln.VulnerabilityID, vuln.PkgName, vuln.InstalledVersion)
|
||||
if old, ok := uniqVulns[key]; ok && !shouldOverwrite(old, vuln) {
|
||||
continue
|
||||
}
|
||||
uniqVulns[key] = vuln
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return toSlice(uniqVulns)
|
||||
}
|
||||
|
||||
func toSlice(uniqVulns map[string]types.DetectedVulnerability) []types.DetectedVulnerability {
|
||||
// Convert map to slice
|
||||
var vulnerabilities []types.DetectedVulnerability
|
||||
for _, vuln := range uniqVulns {
|
||||
vulnerabilities = append(vulnerabilities, vuln)
|
||||
}
|
||||
|
||||
return vulnerabilities
|
||||
}
|
||||
|
||||
func applyPolicy(ctx context.Context, vulns []types.DetectedVulnerability, policyFile string) ([]types.DetectedVulnerability, error) {
|
||||
policy, err := ioutil.ReadFile(policyFile)
|
||||
if err != nil {
|
||||
@@ -236,3 +263,8 @@ func getIgnoredIDs(ignoreFile string) []string {
|
||||
}
|
||||
return ignoredIDs
|
||||
}
|
||||
|
||||
func shouldOverwrite(old, new types.DetectedVulnerability) bool {
|
||||
// The same vulnerability must be picked always.
|
||||
return old.FixedVersion < new.FixedVersion
|
||||
}
|
||||
|
||||
@@ -4,48 +4,32 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
ftypes "github.com/aquasecurity/fanal/types"
|
||||
"github.com/aquasecurity/trivy-db/pkg/db"
|
||||
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy-db/pkg/utils"
|
||||
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
|
||||
"github.com/aquasecurity/trivy/pkg/dbtest"
|
||||
"github.com/aquasecurity/trivy/pkg/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestClient_FillInfo(t *testing.T) {
|
||||
func TestClient_FillVulnerabilityInfo(t *testing.T) {
|
||||
type args struct {
|
||||
vulns []types.DetectedVulnerability
|
||||
reportType string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
getSeverity []db.OperationGetSeverityExpectation
|
||||
getVulnerability []db.OperationGetVulnerabilityExpectation
|
||||
fixtures []string
|
||||
args args
|
||||
expectedVulnerabilities []types.DetectedVulnerability
|
||||
}{
|
||||
{
|
||||
name: "happy path, with only OS vulnerability but no vendor severity, no NVD",
|
||||
getVulnerability: []db.OperationGetVulnerabilityExpectation{
|
||||
{
|
||||
Args: db.OperationGetVulnerabilityArgs{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
},
|
||||
Returns: db.OperationGetVulnerabilityReturns{
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "dos",
|
||||
Description: "dos vulnerability",
|
||||
Severity: dbTypes.SeverityMedium.String(),
|
||||
References: []string{"http://example.com"},
|
||||
LastModifiedDate: utils.MustTimeParse("2020-01-01T01:01:00Z"),
|
||||
PublishedDate: utils.MustTimeParse("2001-01-01T01:01:00Z"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
name: "happy path, with only OS vulnerability but no vendor severity, no NVD",
|
||||
fixtures: []string{"testdata/fixtures/full.yaml"},
|
||||
args: args{
|
||||
vulns: []types.DetectedVulnerability{
|
||||
{VulnerabilityID: "CVE-2019-0001"},
|
||||
@@ -68,36 +52,17 @@ func TestClient_FillInfo(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path, with only OS vulnerability but no vendor severity, yes NVD",
|
||||
getVulnerability: []db.OperationGetVulnerabilityExpectation{
|
||||
{
|
||||
Args: db.OperationGetVulnerabilityArgs{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
},
|
||||
Returns: db.OperationGetVulnerabilityReturns{
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "dos",
|
||||
Description: "dos vulnerability",
|
||||
Severity: dbTypes.SeverityUnknown.String(),
|
||||
VendorSeverity: dbTypes.VendorSeverity{
|
||||
vulnerability.Nvd: dbTypes.SeverityLow,
|
||||
},
|
||||
References: []string{"http://example.com"},
|
||||
LastModifiedDate: utils.MustTimeParse("2020-01-01T01:01:00Z"),
|
||||
PublishedDate: utils.MustTimeParse("2001-01-01T01:01:00Z"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
name: "happy path, with only OS vulnerability but no vendor severity, yes NVD",
|
||||
fixtures: []string{"testdata/fixtures/full.yaml"},
|
||||
args: args{
|
||||
vulns: []types.DetectedVulnerability{
|
||||
{VulnerabilityID: "CVE-2019-0001"},
|
||||
{VulnerabilityID: "CVE-2019-0002"},
|
||||
},
|
||||
reportType: vulnerability.Ubuntu,
|
||||
},
|
||||
expectedVulnerabilities: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
VulnerabilityID: "CVE-2019-0002",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "dos",
|
||||
Description: "dos vulnerability",
|
||||
@@ -107,89 +72,44 @@ func TestClient_FillInfo(t *testing.T) {
|
||||
PublishedDate: utils.MustTimeParse("2001-01-01T01:01:00Z"),
|
||||
},
|
||||
SeveritySource: vulnerability.Nvd,
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2019-0001",
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2019-0002",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path, with only OS vulnerability but no severity, no vendor severity, no NVD",
|
||||
getVulnerability: []db.OperationGetVulnerabilityExpectation{
|
||||
{
|
||||
Args: db.OperationGetVulnerabilityArgs{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
},
|
||||
Returns: db.OperationGetVulnerabilityReturns{
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "dos",
|
||||
Description: "dos vulnerability",
|
||||
References: []string{"http://example.com"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
name: "happy path, with only OS vulnerability but no severity, no vendor severity, no NVD",
|
||||
fixtures: []string{"testdata/fixtures/full.yaml"},
|
||||
args: args{
|
||||
vulns: []types.DetectedVulnerability{
|
||||
{VulnerabilityID: "CVE-2019-0001"},
|
||||
{VulnerabilityID: "CVE-2019-0003"},
|
||||
},
|
||||
reportType: vulnerability.Ubuntu,
|
||||
},
|
||||
expectedVulnerabilities: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
VulnerabilityID: "CVE-2019-0003",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "dos",
|
||||
Description: "dos vulnerability",
|
||||
Severity: dbTypes.SeverityUnknown.String(),
|
||||
References: []string{"http://example.com"},
|
||||
},
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2019-0001",
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2019-0003",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path, with only OS vulnerability, yes vendor severity, with both NVD and CVSS info",
|
||||
getVulnerability: []db.OperationGetVulnerabilityExpectation{
|
||||
{
|
||||
Args: db.OperationGetVulnerabilityArgs{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
},
|
||||
Returns: db.OperationGetVulnerabilityReturns{
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "dos",
|
||||
Description: "dos vulnerability",
|
||||
Severity: dbTypes.SeverityMedium.String(),
|
||||
CweIDs: []string{"CWE-311"},
|
||||
VendorSeverity: dbTypes.VendorSeverity{
|
||||
vulnerability.RedHat: dbTypes.SeverityLow, // CentOS uses RedHat
|
||||
},
|
||||
CVSS: map[string]dbTypes.CVSS{
|
||||
vulnerability.Nvd: {
|
||||
V2Vector: "(AV:N/AC:L/Au:N/C:P/I:P/A:P)",
|
||||
V2Score: 4.5,
|
||||
V3Vector: "CVSS:3.0/PR:N/UI:N/S:U/C:H/I:H/A:H",
|
||||
V3Score: 5.6,
|
||||
},
|
||||
vulnerability.RedHat: {
|
||||
V2Vector: "AV:N/AC:M/Au:N/C:N/I:P/A:N",
|
||||
V2Score: 7.8,
|
||||
V3Vector: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
|
||||
V3Score: 9.8,
|
||||
},
|
||||
},
|
||||
References: []string{"http://example.com"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
name: "happy path, with only OS vulnerability, yes vendor severity, with both NVD and CVSS info",
|
||||
fixtures: []string{"testdata/fixtures/full.yaml"},
|
||||
args: args{
|
||||
vulns: []types.DetectedVulnerability{
|
||||
{VulnerabilityID: "CVE-2019-0001"},
|
||||
{VulnerabilityID: "CVE-2019-0004"},
|
||||
},
|
||||
reportType: vulnerability.CentOS,
|
||||
},
|
||||
expectedVulnerabilities: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
VulnerabilityID: "CVE-2019-0004",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "dos",
|
||||
Description: "dos vulnerability",
|
||||
@@ -198,7 +118,7 @@ func TestClient_FillInfo(t *testing.T) {
|
||||
References: []string{"http://example.com"},
|
||||
CVSS: map[string]dbTypes.CVSS{
|
||||
vulnerability.Nvd: {
|
||||
V2Vector: "(AV:N/AC:L/Au:N/C:P/I:P/A:P)",
|
||||
V2Vector: "AV:N/AC:L/Au:N/C:P/I:P/A:P",
|
||||
V2Score: 4.5,
|
||||
V3Vector: "CVSS:3.0/PR:N/UI:N/S:U/C:H/I:H/A:H",
|
||||
V3Score: 5.6,
|
||||
@@ -212,104 +132,63 @@ func TestClient_FillInfo(t *testing.T) {
|
||||
},
|
||||
},
|
||||
SeveritySource: vulnerability.RedHat,
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2019-0001",
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2019-0004",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path light db, with only OS vulnerability, yes vendor severity",
|
||||
getVulnerability: []db.OperationGetVulnerabilityExpectation{
|
||||
{
|
||||
Args: db.OperationGetVulnerabilityArgs{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
},
|
||||
Returns: db.OperationGetVulnerabilityReturns{
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityMedium.String(),
|
||||
VendorSeverity: dbTypes.VendorSeverity{
|
||||
vulnerability.Ubuntu: dbTypes.SeverityLow,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
name: "happy path light db, with only OS vulnerability, yes vendor severity",
|
||||
fixtures: []string{"testdata/fixtures/light.yaml"},
|
||||
args: args{
|
||||
vulns: []types.DetectedVulnerability{
|
||||
{VulnerabilityID: "CVE-2019-0001"},
|
||||
{VulnerabilityID: "CVE-2020-0001"},
|
||||
},
|
||||
reportType: vulnerability.Ubuntu,
|
||||
},
|
||||
expectedVulnerabilities: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
VulnerabilityID: "CVE-2020-0001",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "dos",
|
||||
Severity: dbTypes.SeverityLow.String(),
|
||||
},
|
||||
SeveritySource: vulnerability.Ubuntu,
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2019-0001",
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path light db, with only OS vulnerability, no vendor severity",
|
||||
getVulnerability: []db.OperationGetVulnerabilityExpectation{
|
||||
{
|
||||
Args: db.OperationGetVulnerabilityArgs{
|
||||
VulnerabilityID: "CVE-2020-28928",
|
||||
},
|
||||
Returns: db.OperationGetVulnerabilityReturns{
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
VendorSeverity: dbTypes.VendorSeverity{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
name: "happy path light db, with only OS vulnerability, no vendor severity",
|
||||
fixtures: []string{"testdata/fixtures/light.yaml"},
|
||||
args: args{
|
||||
vulns: []types.DetectedVulnerability{
|
||||
{VulnerabilityID: "CVE-2020-28928"},
|
||||
{VulnerabilityID: "CVE-2020-0002"},
|
||||
},
|
||||
reportType: vulnerability.Alpine,
|
||||
},
|
||||
expectedVulnerabilities: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2020-28928",
|
||||
VulnerabilityID: "CVE-2020-0002",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "dos",
|
||||
Severity: dbTypes.SeverityUnknown.String(),
|
||||
},
|
||||
SeveritySource: "",
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-28928",
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0002",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path, with only library vulnerability",
|
||||
getVulnerability: []db.OperationGetVulnerabilityExpectation{
|
||||
{
|
||||
Args: db.OperationGetVulnerabilityArgs{
|
||||
VulnerabilityID: "CVE-2020-0001",
|
||||
},
|
||||
Returns: db.OperationGetVulnerabilityReturns{
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "COVID-19",
|
||||
Description: "a nasty virus vulnerability for humans",
|
||||
Severity: dbTypes.SeverityMedium.String(),
|
||||
VendorSeverity: dbTypes.VendorSeverity{
|
||||
vulnerability.PythonSafetyDB: dbTypes.SeverityCritical,
|
||||
},
|
||||
References: []string{"https://www.who.int/emergencies/diseases/novel-coronavirus-2019"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
name: "happy path, with only library vulnerability",
|
||||
fixtures: []string{"testdata/fixtures/full.yaml"},
|
||||
args: args{
|
||||
vulns: []types.DetectedVulnerability{
|
||||
{VulnerabilityID: "CVE-2020-0001"},
|
||||
{VulnerabilityID: "CVE-2019-0005"},
|
||||
},
|
||||
reportType: "poetry",
|
||||
reportType: ftypes.Poetry,
|
||||
},
|
||||
expectedVulnerabilities: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2020-0001",
|
||||
VulnerabilityID: "CVE-2019-0005",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Title: "COVID-19",
|
||||
Description: "a nasty virus vulnerability for humans",
|
||||
@@ -317,22 +196,13 @@ func TestClient_FillInfo(t *testing.T) {
|
||||
References: []string{"https://www.who.int/emergencies/diseases/novel-coronavirus-2019"},
|
||||
},
|
||||
SeveritySource: vulnerability.PythonSafetyDB,
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2020-0001",
|
||||
PrimaryURL: "https://avd.aquasec.com/nvd/cve-2019-0005",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "GetVulnerability returns an error",
|
||||
getVulnerability: []db.OperationGetVulnerabilityExpectation{
|
||||
{
|
||||
Args: db.OperationGetVulnerabilityArgs{
|
||||
VulnerabilityID: "CVE-2019-0004",
|
||||
},
|
||||
Returns: db.OperationGetVulnerabilityReturns{
|
||||
Err: xerrors.New("failed"),
|
||||
},
|
||||
},
|
||||
},
|
||||
name: "GetVulnerability returns an error",
|
||||
fixtures: []string{"testdata/fixtures/sad.yaml"},
|
||||
args: args{
|
||||
vulns: []types.DetectedVulnerability{
|
||||
{VulnerabilityID: "CVE-2019-0004"},
|
||||
@@ -346,17 +216,15 @@ func TestClient_FillInfo(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
mockDBConfig := new(db.MockOperation)
|
||||
mockDBConfig.ApplyGetSeverityExpectations(tt.getSeverity)
|
||||
mockDBConfig.ApplyGetVulnerabilityExpectations(tt.getVulnerability)
|
||||
dbtest.InitDB(t, tt.fixtures)
|
||||
defer db.Close()
|
||||
|
||||
c := Client{
|
||||
dbc: mockDBConfig,
|
||||
dbc: db.Config{},
|
||||
}
|
||||
|
||||
c.FillInfo(tt.args.vulns, tt.args.reportType)
|
||||
assert.Equal(t, tt.expectedVulnerabilities, tt.args.vulns, tt.name)
|
||||
mockDBConfig.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -452,9 +320,9 @@ func TestClient_Filter(t *testing.T) {
|
||||
policyFile string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want []types.DetectedVulnerability
|
||||
name string
|
||||
args args
|
||||
wantVulns []types.DetectedVulnerability
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
@@ -509,7 +377,7 @@ func TestClient_Filter(t *testing.T) {
|
||||
severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityHigh, dbTypes.SeverityUnknown},
|
||||
ignoreUnfixed: false,
|
||||
},
|
||||
want: []types.DetectedVulnerability{
|
||||
wantVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2018-0001",
|
||||
PkgName: "bar",
|
||||
@@ -613,7 +481,7 @@ func TestClient_Filter(t *testing.T) {
|
||||
ignoreUnfixed: false,
|
||||
ignoreFile: "testdata/.trivyignore",
|
||||
},
|
||||
want: []types.DetectedVulnerability{
|
||||
wantVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0003",
|
||||
PkgName: "foo",
|
||||
@@ -663,7 +531,7 @@ func TestClient_Filter(t *testing.T) {
|
||||
ignoreUnfixed: false,
|
||||
policyFile: "./testdata/test.rego",
|
||||
},
|
||||
want: []types.DetectedVulnerability{
|
||||
wantVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
PkgName: "foo",
|
||||
@@ -675,14 +543,141 @@ func TestClient_Filter(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path with duplicates, one with empty fixed version",
|
||||
args: args{
|
||||
vulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityLow.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0001",
|
||||
PkgName: "foo",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "1.2.4",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityLow.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0002",
|
||||
PkgName: "bar",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "1.2.4",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityCritical.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0002",
|
||||
PkgName: "bar",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "1.2.5",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityCritical.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2018-0001",
|
||||
PkgName: "baz",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityHigh.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2018-0001",
|
||||
PkgName: "bar",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityCritical.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2018-0002",
|
||||
PkgName: "bar",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2018-0002",
|
||||
PkgName: "bar",
|
||||
InstalledVersion: "2.0.0",
|
||||
FixedVersion: "",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
severities: []dbTypes.Severity{dbTypes.SeverityCritical, dbTypes.SeverityHigh, dbTypes.SeverityUnknown},
|
||||
ignoreUnfixed: false,
|
||||
},
|
||||
wantVulns: []types.DetectedVulnerability{
|
||||
{
|
||||
VulnerabilityID: "CVE-2018-0001",
|
||||
PkgName: "bar",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityCritical.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2019-0002",
|
||||
PkgName: "bar",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "1.2.5",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityCritical.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2018-0002",
|
||||
PkgName: "bar",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityUnknown.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2018-0002",
|
||||
PkgName: "bar",
|
||||
InstalledVersion: "2.0.0",
|
||||
FixedVersion: "",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityUnknown.String(),
|
||||
},
|
||||
},
|
||||
{
|
||||
VulnerabilityID: "CVE-2018-0001",
|
||||
PkgName: "baz",
|
||||
InstalledVersion: "1.2.3",
|
||||
FixedVersion: "",
|
||||
Vulnerability: dbTypes.Vulnerability{
|
||||
Severity: dbTypes.SeverityHigh.String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := Client{}
|
||||
got, err := c.Filter(context.Background(), tt.args.vulns, tt.args.severities,
|
||||
tt.args.ignoreUnfixed, tt.args.ignoreFile, tt.args.policyFile)
|
||||
gotVulns, err := c.Filter(context.Background(), tt.args.vulns, tt.args.severities, tt.args.ignoreUnfixed, tt.args.ignoreFile, tt.args.policyFile)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, got, tt.name)
|
||||
assert.Equal(t, tt.wantVulns, gotVulns)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
24
rpc/cache/service.pb.go
vendored
24
rpc/cache/service.pb.go
vendored
@@ -7,8 +7,8 @@ import (
|
||||
fmt "fmt"
|
||||
common "github.com/aquasecurity/trivy/rpc/common"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
_ "github.com/golang/protobuf/ptypes/empty"
|
||||
timestamp "github.com/golang/protobuf/ptypes/timestamp"
|
||||
_ "google.golang.org/protobuf/types/known/emptypb"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
math "math"
|
||||
)
|
||||
|
||||
@@ -24,15 +24,15 @@ var _ = math.Inf
|
||||
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
|
||||
|
||||
type ArtifactInfo struct {
|
||||
SchemaVersion int32 `protobuf:"varint,1,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"`
|
||||
Architecture string `protobuf:"bytes,2,opt,name=architecture,proto3" json:"architecture,omitempty"`
|
||||
Created *timestamp.Timestamp `protobuf:"bytes,3,opt,name=created,proto3" json:"created,omitempty"`
|
||||
DockerVersion string `protobuf:"bytes,4,opt,name=docker_version,json=dockerVersion,proto3" json:"docker_version,omitempty"`
|
||||
Os string `protobuf:"bytes,5,opt,name=os,proto3" json:"os,omitempty"`
|
||||
HistoryPackages []*common.Package `protobuf:"bytes,6,rep,name=history_packages,json=historyPackages,proto3" json:"history_packages,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
SchemaVersion int32 `protobuf:"varint,1,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"`
|
||||
Architecture string `protobuf:"bytes,2,opt,name=architecture,proto3" json:"architecture,omitempty"`
|
||||
Created *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=created,proto3" json:"created,omitempty"`
|
||||
DockerVersion string `protobuf:"bytes,4,opt,name=docker_version,json=dockerVersion,proto3" json:"docker_version,omitempty"`
|
||||
Os string `protobuf:"bytes,5,opt,name=os,proto3" json:"os,omitempty"`
|
||||
HistoryPackages []*common.Package `protobuf:"bytes,6,rep,name=history_packages,json=historyPackages,proto3" json:"history_packages,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *ArtifactInfo) Reset() { *m = ArtifactInfo{} }
|
||||
@@ -74,7 +74,7 @@ func (m *ArtifactInfo) GetArchitecture() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *ArtifactInfo) GetCreated() *timestamp.Timestamp {
|
||||
func (m *ArtifactInfo) GetCreated() *timestamppb.Timestamp {
|
||||
if m != nil {
|
||||
return m.Created
|
||||
}
|
||||
|
||||
77
rpc/cache/service.twirp.go
vendored
77
rpc/cache/service.twirp.go
vendored
@@ -17,23 +17,19 @@ import fmt "fmt"
|
||||
import ioutil "io/ioutil"
|
||||
import http "net/http"
|
||||
import strconv "strconv"
|
||||
import gzip "compress/gzip"
|
||||
|
||||
import jsonpb "github.com/golang/protobuf/jsonpb"
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import twirp "github.com/twitchtv/twirp"
|
||||
import ctxsetters "github.com/twitchtv/twirp/ctxsetters"
|
||||
|
||||
import google_protobuf1 "github.com/golang/protobuf/ptypes/empty"
|
||||
import google_protobuf1 "google.golang.org/protobuf/types/known/emptypb"
|
||||
|
||||
// Imports only used by utility functions:
|
||||
import io "io"
|
||||
import json "encoding/json"
|
||||
import url "net/url"
|
||||
|
||||
// A response is compressed with gzip when the response size exceeds this threshold.
|
||||
const CompressThreshold = 10000
|
||||
|
||||
// ===============
|
||||
// Cache Interface
|
||||
// ===============
|
||||
@@ -420,16 +416,6 @@ func (s *cacheServer) servePutArtifactProtobuf(ctx context.Context, resp http.Re
|
||||
return
|
||||
}
|
||||
|
||||
// Compress the response if the size exceeds the threshold
|
||||
if len(respBytes) > CompressThreshold && isGZipAcceptable(req) {
|
||||
respBytes, err = compressWithGzip(respBytes)
|
||||
if err != nil {
|
||||
s.writeError(ctx, resp, wrapInternal(err, "failed to compress response"))
|
||||
return
|
||||
}
|
||||
resp.Header().Set("Content-Encoding", "gzip")
|
||||
}
|
||||
|
||||
ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
|
||||
resp.Header().Set("Content-Type", "application/protobuf")
|
||||
resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
|
||||
@@ -559,16 +545,6 @@ func (s *cacheServer) servePutBlobProtobuf(ctx context.Context, resp http.Respon
|
||||
return
|
||||
}
|
||||
|
||||
// Compress the response if the size exceeds the threshold
|
||||
if len(respBytes) > CompressThreshold && isGZipAcceptable(req) {
|
||||
respBytes, err = compressWithGzip(respBytes)
|
||||
if err != nil {
|
||||
s.writeError(ctx, resp, wrapInternal(err, "failed to compress response"))
|
||||
return
|
||||
}
|
||||
resp.Header().Set("Content-Encoding", "gzip")
|
||||
}
|
||||
|
||||
ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
|
||||
resp.Header().Set("Content-Type", "application/protobuf")
|
||||
resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
|
||||
@@ -698,16 +674,6 @@ func (s *cacheServer) serveMissingBlobsProtobuf(ctx context.Context, resp http.R
|
||||
return
|
||||
}
|
||||
|
||||
// Compress the response if the size exceeds the threshold
|
||||
if len(respBytes) > CompressThreshold && isGZipAcceptable(req) {
|
||||
respBytes, err = compressWithGzip(respBytes)
|
||||
if err != nil {
|
||||
s.writeError(ctx, resp, wrapInternal(err, "failed to compress response"))
|
||||
return
|
||||
}
|
||||
resp.Header().Set("Content-Encoding", "gzip")
|
||||
}
|
||||
|
||||
ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
|
||||
resp.Header().Set("Content-Type", "application/protobuf")
|
||||
resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
|
||||
@@ -866,7 +832,6 @@ func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType
|
||||
}
|
||||
req.Header.Set("Accept", contentType)
|
||||
req.Header.Set("Content-Type", contentType)
|
||||
req.Header.Set("Accept-Encoding", "gzip")
|
||||
req.Header.Set("Twirp-Version", "v5.10.1")
|
||||
return req, nil
|
||||
}
|
||||
@@ -1119,15 +1084,7 @@ func doProtobufRequest(ctx context.Context, client HTTPClient, hooks *twirp.Clie
|
||||
return errorFromResponse(resp)
|
||||
}
|
||||
|
||||
r := resp.Body
|
||||
if resp.Header.Get("Content-Encoding") == "gzip" {
|
||||
r, err = gzip.NewReader(r)
|
||||
if err != nil {
|
||||
return wrapInternal(err, "invalid gzip")
|
||||
}
|
||||
}
|
||||
|
||||
respBodyBytes, err := ioutil.ReadAll(r)
|
||||
respBodyBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return wrapInternal(err, "failed to read response body")
|
||||
}
|
||||
@@ -1232,36 +1189,6 @@ func callError(ctx context.Context, h *twirp.ServerHooks, err twirp.Error) conte
|
||||
return h.Error(ctx, err)
|
||||
}
|
||||
|
||||
// compressWithGzip compresses the data with gzip
|
||||
func compressWithGzip(data []byte) ([]byte, error) {
|
||||
var b bytes.Buffer
|
||||
gz := gzip.NewWriter(&b)
|
||||
defer gz.Close()
|
||||
|
||||
if _, err := gz.Write(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := gz.Flush(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := gz.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
func isGZipAcceptable(request *http.Request) bool {
|
||||
for _, encoding := range request.Header["Accept-Encoding"] {
|
||||
if encoding == "gzip" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func callClientResponseReceived(ctx context.Context, h *twirp.ClientHooks) {
|
||||
if h == nil || h.ResponseReceived == nil {
|
||||
return
|
||||
|
||||
@@ -6,7 +6,7 @@ package common
|
||||
import (
|
||||
fmt "fmt"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
timestamp "github.com/golang/protobuf/ptypes/timestamp"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
math "math"
|
||||
)
|
||||
|
||||
@@ -359,24 +359,24 @@ func (m *Library) GetVersion() string {
|
||||
}
|
||||
|
||||
type Vulnerability struct {
|
||||
VulnerabilityId string `protobuf:"bytes,1,opt,name=vulnerability_id,json=vulnerabilityId,proto3" json:"vulnerability_id,omitempty"`
|
||||
PkgName string `protobuf:"bytes,2,opt,name=pkg_name,json=pkgName,proto3" json:"pkg_name,omitempty"`
|
||||
InstalledVersion string `protobuf:"bytes,3,opt,name=installed_version,json=installedVersion,proto3" json:"installed_version,omitempty"`
|
||||
FixedVersion string `protobuf:"bytes,4,opt,name=fixed_version,json=fixedVersion,proto3" json:"fixed_version,omitempty"`
|
||||
Title string `protobuf:"bytes,5,opt,name=title,proto3" json:"title,omitempty"`
|
||||
Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,omitempty"`
|
||||
Severity Severity `protobuf:"varint,7,opt,name=severity,proto3,enum=trivy.common.Severity" json:"severity,omitempty"`
|
||||
References []string `protobuf:"bytes,8,rep,name=references,proto3" json:"references,omitempty"`
|
||||
Layer *Layer `protobuf:"bytes,10,opt,name=layer,proto3" json:"layer,omitempty"`
|
||||
SeveritySource string `protobuf:"bytes,11,opt,name=severity_source,json=severitySource,proto3" json:"severity_source,omitempty"`
|
||||
Cvss map[string]*CVSS `protobuf:"bytes,12,rep,name=cvss,proto3" json:"cvss,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
CweIds []string `protobuf:"bytes,13,rep,name=cwe_ids,json=cweIds,proto3" json:"cwe_ids,omitempty"`
|
||||
PrimaryUrl string `protobuf:"bytes,14,opt,name=primary_url,json=primaryUrl,proto3" json:"primary_url,omitempty"`
|
||||
PublishedDate *timestamp.Timestamp `protobuf:"bytes,15,opt,name=published_date,json=publishedDate,proto3" json:"published_date,omitempty"`
|
||||
LastModifiedDate *timestamp.Timestamp `protobuf:"bytes,16,opt,name=last_modified_date,json=lastModifiedDate,proto3" json:"last_modified_date,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
VulnerabilityId string `protobuf:"bytes,1,opt,name=vulnerability_id,json=vulnerabilityId,proto3" json:"vulnerability_id,omitempty"`
|
||||
PkgName string `protobuf:"bytes,2,opt,name=pkg_name,json=pkgName,proto3" json:"pkg_name,omitempty"`
|
||||
InstalledVersion string `protobuf:"bytes,3,opt,name=installed_version,json=installedVersion,proto3" json:"installed_version,omitempty"`
|
||||
FixedVersion string `protobuf:"bytes,4,opt,name=fixed_version,json=fixedVersion,proto3" json:"fixed_version,omitempty"`
|
||||
Title string `protobuf:"bytes,5,opt,name=title,proto3" json:"title,omitempty"`
|
||||
Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,omitempty"`
|
||||
Severity Severity `protobuf:"varint,7,opt,name=severity,proto3,enum=trivy.common.Severity" json:"severity,omitempty"`
|
||||
References []string `protobuf:"bytes,8,rep,name=references,proto3" json:"references,omitempty"`
|
||||
Layer *Layer `protobuf:"bytes,10,opt,name=layer,proto3" json:"layer,omitempty"`
|
||||
SeveritySource string `protobuf:"bytes,11,opt,name=severity_source,json=severitySource,proto3" json:"severity_source,omitempty"`
|
||||
Cvss map[string]*CVSS `protobuf:"bytes,12,rep,name=cvss,proto3" json:"cvss,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
|
||||
CweIds []string `protobuf:"bytes,13,rep,name=cwe_ids,json=cweIds,proto3" json:"cwe_ids,omitempty"`
|
||||
PrimaryUrl string `protobuf:"bytes,14,opt,name=primary_url,json=primaryUrl,proto3" json:"primary_url,omitempty"`
|
||||
PublishedDate *timestamppb.Timestamp `protobuf:"bytes,15,opt,name=published_date,json=publishedDate,proto3" json:"published_date,omitempty"`
|
||||
LastModifiedDate *timestamppb.Timestamp `protobuf:"bytes,16,opt,name=last_modified_date,json=lastModifiedDate,proto3" json:"last_modified_date,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (m *Vulnerability) Reset() { *m = Vulnerability{} }
|
||||
@@ -495,14 +495,14 @@ func (m *Vulnerability) GetPrimaryUrl() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *Vulnerability) GetPublishedDate() *timestamp.Timestamp {
|
||||
func (m *Vulnerability) GetPublishedDate() *timestamppb.Timestamp {
|
||||
if m != nil {
|
||||
return m.PublishedDate
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Vulnerability) GetLastModifiedDate() *timestamp.Timestamp {
|
||||
func (m *Vulnerability) GetLastModifiedDate() *timestamppb.Timestamp {
|
||||
if m != nil {
|
||||
return m.LastModifiedDate
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ func (m *ScanRequest) GetOptions() *ScanOptions {
|
||||
|
||||
type ScanOptions struct {
|
||||
VulnType []string `protobuf:"bytes,1,rep,name=vuln_type,json=vulnType,proto3" json:"vuln_type,omitempty"`
|
||||
SecurityChecks []string `protobuf:"bytes,2,rep,name=security_checks,json=securityChecks,proto3" json:"security_checks,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
@@ -123,6 +124,13 @@ func (m *ScanOptions) GetVulnType() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ScanOptions) GetSecurityChecks() []string {
|
||||
if m != nil {
|
||||
return m.SecurityChecks
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ScanResponse struct {
|
||||
Os *common.OS `protobuf:"bytes,1,opt,name=os,proto3" json:"os,omitempty"`
|
||||
Eosl bool `protobuf:"varint,2,opt,name=eosl,proto3" json:"eosl,omitempty"`
|
||||
@@ -244,29 +252,30 @@ func init() {
|
||||
func init() { proto.RegisterFile("rpc/scanner/service.proto", fileDescriptor_60d0e837512b18d4) }
|
||||
|
||||
var fileDescriptor_60d0e837512b18d4 = []byte{
|
||||
// 377 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xc1, 0x6b, 0xdb, 0x30,
|
||||
0x14, 0xc6, 0xb1, 0x13, 0xe2, 0xf8, 0x79, 0xb0, 0xa0, 0xc3, 0x70, 0x12, 0xb6, 0x19, 0x9f, 0xc2,
|
||||
0x0e, 0x36, 0xf3, 0x60, 0xbb, 0x0f, 0x72, 0xc8, 0x29, 0x45, 0x29, 0x3d, 0xf4, 0x12, 0x64, 0x59,
|
||||
0x4d, 0x05, 0x8e, 0xe5, 0x48, 0xb2, 0xa9, 0xe9, 0x7f, 0xd2, 0xbf, 0xb6, 0x58, 0x72, 0xa0, 0x49,
|
||||
0xc9, 0xed, 0xe9, 0xbd, 0xcf, 0x4f, 0xbf, 0xef, 0xb3, 0x60, 0x2e, 0x6b, 0x9a, 0x2a, 0x4a, 0xaa,
|
||||
0x8a, 0xc9, 0x54, 0x31, 0xd9, 0x72, 0xca, 0x92, 0x5a, 0x0a, 0x2d, 0xd0, 0x4c, 0x4b, 0xde, 0x76,
|
||||
0xc9, 0x30, 0x4c, 0xda, 0xdf, 0x8b, 0xbf, 0x07, 0xae, 0x9f, 0x9b, 0x3c, 0xa1, 0xe2, 0x98, 0x92,
|
||||
0x53, 0x43, 0x14, 0xa3, 0x8d, 0xe4, 0xba, 0x4b, 0x8d, 0x32, 0xed, 0x57, 0x51, 0x71, 0x3c, 0x8a,
|
||||
0xea, 0x72, 0x53, 0xfc, 0xe6, 0x40, 0xb0, 0xa3, 0xa4, 0xc2, 0xec, 0xd4, 0x30, 0xa5, 0xd1, 0x37,
|
||||
0x98, 0x68, 0x22, 0x0f, 0x4c, 0x87, 0x4e, 0xe4, 0xac, 0x7c, 0x3c, 0x9c, 0xd0, 0x4f, 0x08, 0x88,
|
||||
0xd4, 0xfc, 0x89, 0x50, 0xbd, 0xe7, 0x45, 0xe8, 0x9a, 0x21, 0x9c, 0x5b, 0x9b, 0x02, 0xcd, 0x61,
|
||||
0x9a, 0x97, 0x22, 0xdf, 0xf3, 0x42, 0x85, 0xa3, 0x68, 0xb4, 0xf2, 0xb1, 0xd7, 0x9f, 0x37, 0x85,
|
||||
0x42, 0xff, 0xc0, 0x13, 0xb5, 0xe6, 0xa2, 0x52, 0xe1, 0x38, 0x72, 0x56, 0x41, 0xf6, 0x3d, 0xb9,
|
||||
0xe6, 0x4f, 0x7a, 0x86, 0xad, 0x15, 0xe1, 0xb3, 0x3a, 0xfe, 0x65, 0xd9, 0x86, 0x3e, 0x5a, 0x82,
|
||||
0xdf, 0x36, 0x65, 0xb5, 0xd7, 0x5d, 0xcd, 0x42, 0xc7, 0xdc, 0x31, 0xed, 0x1b, 0xf7, 0x5d, 0xcd,
|
||||
0xe2, 0x17, 0xf8, 0x62, 0x7d, 0xa8, 0x5a, 0x54, 0x8a, 0xa1, 0x08, 0x5c, 0xa1, 0x8c, 0x89, 0x20,
|
||||
0x9b, 0x0d, 0xf7, 0xd9, 0x04, 0x92, 0xed, 0x0e, 0xbb, 0x42, 0x21, 0x04, 0x63, 0x26, 0x54, 0x69,
|
||||
0xbc, 0x4c, 0xb1, 0xa9, 0x51, 0x06, 0x9e, 0x64, 0xaa, 0x29, 0xb5, 0x35, 0x11, 0x64, 0xe1, 0x67,
|
||||
0x54, 0x6c, 0x04, 0xf8, 0x2c, 0x8c, 0x5f, 0x61, 0x62, 0x5b, 0x37, 0xc3, 0x5b, 0xc3, 0xd7, 0x9e,
|
||||
0x93, 0x49, 0x92, 0xf3, 0x92, 0x6b, 0xce, 0x54, 0xe8, 0x9a, 0xed, 0xcb, 0x4b, 0xb0, 0x87, 0x0f,
|
||||
0xa2, 0x0e, 0x5f, 0x7f, 0xd3, 0x03, 0x1b, 0xeb, 0x23, 0xb3, 0xdc, 0xd4, 0xd9, 0x1d, 0x78, 0x3b,
|
||||
0x8b, 0x86, 0xd6, 0x30, 0xee, 0x4b, 0x74, 0x23, 0xdd, 0xe1, 0x0f, 0x2f, 0x7e, 0xdc, 0x1a, 0xdb,
|
||||
0xe0, 0xfe, 0xfb, 0x8f, 0xde, 0x30, 0xca, 0x27, 0xe6, 0x8d, 0xfc, 0x79, 0x0f, 0x00, 0x00, 0xff,
|
||||
0xff, 0xdf, 0x43, 0x89, 0x65, 0x8a, 0x02, 0x00, 0x00,
|
||||
// 395 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0x41, 0x8f, 0x9b, 0x30,
|
||||
0x10, 0x85, 0x05, 0x89, 0x42, 0x18, 0xaa, 0xee, 0xca, 0x87, 0x8a, 0xdd, 0x55, 0x5b, 0xc4, 0xa5,
|
||||
0x39, 0x81, 0x4a, 0xa5, 0xf6, 0xde, 0x2a, 0x87, 0x9c, 0x52, 0x99, 0xaa, 0x87, 0x5e, 0x90, 0x31,
|
||||
0x6e, 0x62, 0x95, 0x60, 0x62, 0x1b, 0x54, 0xd4, 0x7f, 0xd2, 0x5f, 0x5b, 0x61, 0x83, 0xd4, 0x64,
|
||||
0x95, 0xdb, 0xf8, 0xcd, 0x63, 0x78, 0xdf, 0xd8, 0xf0, 0x20, 0x5b, 0x9a, 0x2a, 0x4a, 0x9a, 0x86,
|
||||
0xc9, 0x54, 0x31, 0xd9, 0x73, 0xca, 0x92, 0x56, 0x0a, 0x2d, 0xd0, 0xbd, 0x96, 0xbc, 0x1f, 0x92,
|
||||
0xa9, 0x99, 0xf4, 0xef, 0x1f, 0x3f, 0x1e, 0xb8, 0x3e, 0x76, 0x65, 0x42, 0xc5, 0x29, 0x25, 0xe7,
|
||||
0x8e, 0x28, 0x46, 0x3b, 0xc9, 0xf5, 0x90, 0x1a, 0x67, 0x3a, 0x8e, 0xa2, 0xe2, 0x74, 0x12, 0xcd,
|
||||
0xe5, 0xa4, 0xf8, 0xaf, 0x03, 0x41, 0x4e, 0x49, 0x83, 0xd9, 0xb9, 0x63, 0x4a, 0xa3, 0x57, 0xb0,
|
||||
0xd2, 0x44, 0x1e, 0x98, 0x0e, 0x9d, 0xc8, 0xd9, 0xf8, 0x78, 0x3a, 0xa1, 0xb7, 0x10, 0x10, 0xa9,
|
||||
0xf9, 0x4f, 0x42, 0x75, 0xc1, 0xab, 0xd0, 0x35, 0x4d, 0x98, 0xa5, 0x5d, 0x85, 0x1e, 0x60, 0x5d,
|
||||
0xd6, 0xa2, 0x2c, 0x78, 0xa5, 0xc2, 0x45, 0xb4, 0xd8, 0xf8, 0xd8, 0x1b, 0xcf, 0xbb, 0x4a, 0xa1,
|
||||
0x4f, 0xe0, 0x89, 0x56, 0x73, 0xd1, 0xa8, 0x70, 0x19, 0x39, 0x9b, 0x20, 0x7b, 0x9d, 0x5c, 0xe7,
|
||||
0x4f, 0xc6, 0x0c, 0x7b, 0x6b, 0xc2, 0xb3, 0x3b, 0xce, 0x6d, 0xb6, 0x49, 0x47, 0x4f, 0xe0, 0xf7,
|
||||
0x5d, 0xdd, 0x14, 0x7a, 0x68, 0x59, 0xe8, 0x98, 0x7f, 0xac, 0x47, 0xe1, 0xdb, 0xd0, 0x32, 0xf4,
|
||||
0x0e, 0xee, 0x66, 0xe6, 0x82, 0x1e, 0x19, 0xfd, 0xa5, 0x42, 0xd7, 0x58, 0x5e, 0xce, 0xf2, 0x17,
|
||||
0xa3, 0xc6, 0xbf, 0xe1, 0x85, 0x05, 0x56, 0xad, 0x68, 0x14, 0x43, 0x11, 0xb8, 0x42, 0x19, 0xda,
|
||||
0x20, 0xbb, 0x9f, 0x82, 0xd9, 0x55, 0x25, 0xfb, 0x1c, 0xbb, 0x42, 0x21, 0x04, 0x4b, 0x26, 0x54,
|
||||
0x6d, 0xa0, 0xd7, 0xd8, 0xd4, 0x28, 0x03, 0x4f, 0x32, 0xd5, 0xd5, 0xda, 0xd2, 0x06, 0x59, 0xf8,
|
||||
0x9c, 0x09, 0x1b, 0x03, 0x9e, 0x8d, 0xf1, 0x1f, 0x58, 0x59, 0xe9, 0xe6, 0x96, 0xb7, 0x70, 0x37,
|
||||
0x02, 0x31, 0x49, 0x4a, 0x5e, 0x73, 0xcd, 0x99, 0x85, 0x08, 0xb2, 0xa7, 0xcb, 0x60, 0xdf, 0xff,
|
||||
0x33, 0x0d, 0xf8, 0xfa, 0x9b, 0x31, 0xb0, 0xd9, 0xd1, 0xc2, 0x0c, 0x37, 0x75, 0xf6, 0x15, 0xbc,
|
||||
0xdc, 0x46, 0x43, 0x5b, 0x58, 0x8e, 0x25, 0xba, 0x71, 0x0d, 0xd3, 0x53, 0x78, 0x7c, 0x73, 0xab,
|
||||
0x6d, 0x17, 0xf7, 0xd9, 0xff, 0xe1, 0x4d, 0xad, 0x72, 0x65, 0x1e, 0xd3, 0x87, 0x7f, 0x01, 0x00,
|
||||
0x00, 0xff, 0xff, 0xfd, 0x72, 0xf0, 0x54, 0xb3, 0x02, 0x00, 0x00,
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@ message ScanRequest {
|
||||
}
|
||||
|
||||
message ScanOptions {
|
||||
repeated string vuln_type = 1;
|
||||
repeated string vuln_type = 1;
|
||||
repeated string security_checks = 2;
|
||||
}
|
||||
|
||||
message ScanResponse {
|
||||
|
||||
@@ -17,7 +17,6 @@ import fmt "fmt"
|
||||
import ioutil "io/ioutil"
|
||||
import http "net/http"
|
||||
import strconv "strconv"
|
||||
import gzip "compress/gzip"
|
||||
|
||||
import jsonpb "github.com/golang/protobuf/jsonpb"
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
@@ -29,9 +28,6 @@ import io "io"
|
||||
import json "encoding/json"
|
||||
import url "net/url"
|
||||
|
||||
// A response is compressed with gzip when the response size exceeds this threshold.
|
||||
const CompressThreshold = 10000
|
||||
|
||||
// =================
|
||||
// Scanner Interface
|
||||
// =================
|
||||
@@ -324,16 +320,6 @@ func (s *scannerServer) serveScanProtobuf(ctx context.Context, resp http.Respons
|
||||
return
|
||||
}
|
||||
|
||||
// Compress the response if the size exceeds the threshold
|
||||
if len(respBytes) > CompressThreshold && isGZipAcceptable(req) {
|
||||
respBytes, err = compressWithGzip(respBytes)
|
||||
if err != nil {
|
||||
s.writeError(ctx, resp, wrapInternal(err, "failed to compress response"))
|
||||
return
|
||||
}
|
||||
resp.Header().Set("Content-Encoding", "gzip")
|
||||
}
|
||||
|
||||
ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
|
||||
resp.Header().Set("Content-Type", "application/protobuf")
|
||||
resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
|
||||
@@ -492,7 +478,6 @@ func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType
|
||||
}
|
||||
req.Header.Set("Accept", contentType)
|
||||
req.Header.Set("Content-Type", contentType)
|
||||
req.Header.Set("Accept-Encoding", "gzip")
|
||||
req.Header.Set("Twirp-Version", "v5.10.1")
|
||||
return req, nil
|
||||
}
|
||||
@@ -745,15 +730,7 @@ func doProtobufRequest(ctx context.Context, client HTTPClient, hooks *twirp.Clie
|
||||
return errorFromResponse(resp)
|
||||
}
|
||||
|
||||
r := resp.Body
|
||||
if resp.Header.Get("Content-Encoding") == "gzip" {
|
||||
r, err = gzip.NewReader(r)
|
||||
if err != nil {
|
||||
return wrapInternal(err, "invalid gzip")
|
||||
}
|
||||
}
|
||||
|
||||
respBodyBytes, err := ioutil.ReadAll(r)
|
||||
respBodyBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return wrapInternal(err, "failed to read response body")
|
||||
}
|
||||
@@ -858,36 +835,6 @@ func callError(ctx context.Context, h *twirp.ServerHooks, err twirp.Error) conte
|
||||
return h.Error(ctx, err)
|
||||
}
|
||||
|
||||
// compressWithGzip compresses the data with gzip
|
||||
func compressWithGzip(data []byte) ([]byte, error) {
|
||||
var b bytes.Buffer
|
||||
gz := gzip.NewWriter(&b)
|
||||
defer gz.Close()
|
||||
|
||||
if _, err := gz.Write(data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := gz.Flush(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := gz.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
func isGZipAcceptable(request *http.Request) bool {
|
||||
for _, encoding := range request.Header["Accept-Encoding"] {
|
||||
if encoding == "gzip" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func callClientResponseReceived(ctx context.Context, h *twirp.ClientHooks) {
|
||||
if h == nil || h.ResponseReceived == nil {
|
||||
return
|
||||
@@ -910,29 +857,30 @@ func callClientError(ctx context.Context, h *twirp.ClientHooks, err twirp.Error)
|
||||
}
|
||||
|
||||
var twirpFileDescriptor0 = []byte{
|
||||
// 377 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xc1, 0x6b, 0xdb, 0x30,
|
||||
0x14, 0xc6, 0xb1, 0x13, 0xe2, 0xf8, 0x79, 0xb0, 0xa0, 0xc3, 0x70, 0x12, 0xb6, 0x19, 0x9f, 0xc2,
|
||||
0x0e, 0x36, 0xf3, 0x60, 0xbb, 0x0f, 0x72, 0xc8, 0x29, 0x45, 0x29, 0x3d, 0xf4, 0x12, 0x64, 0x59,
|
||||
0x4d, 0x05, 0x8e, 0xe5, 0x48, 0xb2, 0xa9, 0xe9, 0x7f, 0xd2, 0xbf, 0xb6, 0x58, 0x72, 0xa0, 0x49,
|
||||
0xc9, 0xed, 0xe9, 0xbd, 0xcf, 0x4f, 0xbf, 0xef, 0xb3, 0x60, 0x2e, 0x6b, 0x9a, 0x2a, 0x4a, 0xaa,
|
||||
0x8a, 0xc9, 0x54, 0x31, 0xd9, 0x72, 0xca, 0x92, 0x5a, 0x0a, 0x2d, 0xd0, 0x4c, 0x4b, 0xde, 0x76,
|
||||
0xc9, 0x30, 0x4c, 0xda, 0xdf, 0x8b, 0xbf, 0x07, 0xae, 0x9f, 0x9b, 0x3c, 0xa1, 0xe2, 0x98, 0x92,
|
||||
0x53, 0x43, 0x14, 0xa3, 0x8d, 0xe4, 0xba, 0x4b, 0x8d, 0x32, 0xed, 0x57, 0x51, 0x71, 0x3c, 0x8a,
|
||||
0xea, 0x72, 0x53, 0xfc, 0xe6, 0x40, 0xb0, 0xa3, 0xa4, 0xc2, 0xec, 0xd4, 0x30, 0xa5, 0xd1, 0x37,
|
||||
0x98, 0x68, 0x22, 0x0f, 0x4c, 0x87, 0x4e, 0xe4, 0xac, 0x7c, 0x3c, 0x9c, 0xd0, 0x4f, 0x08, 0x88,
|
||||
0xd4, 0xfc, 0x89, 0x50, 0xbd, 0xe7, 0x45, 0xe8, 0x9a, 0x21, 0x9c, 0x5b, 0x9b, 0x02, 0xcd, 0x61,
|
||||
0x9a, 0x97, 0x22, 0xdf, 0xf3, 0x42, 0x85, 0xa3, 0x68, 0xb4, 0xf2, 0xb1, 0xd7, 0x9f, 0x37, 0x85,
|
||||
0x42, 0xff, 0xc0, 0x13, 0xb5, 0xe6, 0xa2, 0x52, 0xe1, 0x38, 0x72, 0x56, 0x41, 0xf6, 0x3d, 0xb9,
|
||||
0xe6, 0x4f, 0x7a, 0x86, 0xad, 0x15, 0xe1, 0xb3, 0x3a, 0xfe, 0x65, 0xd9, 0x86, 0x3e, 0x5a, 0x82,
|
||||
0xdf, 0x36, 0x65, 0xb5, 0xd7, 0x5d, 0xcd, 0x42, 0xc7, 0xdc, 0x31, 0xed, 0x1b, 0xf7, 0x5d, 0xcd,
|
||||
0xe2, 0x17, 0xf8, 0x62, 0x7d, 0xa8, 0x5a, 0x54, 0x8a, 0xa1, 0x08, 0x5c, 0xa1, 0x8c, 0x89, 0x20,
|
||||
0x9b, 0x0d, 0xf7, 0xd9, 0x04, 0x92, 0xed, 0x0e, 0xbb, 0x42, 0x21, 0x04, 0x63, 0x26, 0x54, 0x69,
|
||||
0xbc, 0x4c, 0xb1, 0xa9, 0x51, 0x06, 0x9e, 0x64, 0xaa, 0x29, 0xb5, 0x35, 0x11, 0x64, 0xe1, 0x67,
|
||||
0x54, 0x6c, 0x04, 0xf8, 0x2c, 0x8c, 0x5f, 0x61, 0x62, 0x5b, 0x37, 0xc3, 0x5b, 0xc3, 0xd7, 0x9e,
|
||||
0x93, 0x49, 0x92, 0xf3, 0x92, 0x6b, 0xce, 0x54, 0xe8, 0x9a, 0xed, 0xcb, 0x4b, 0xb0, 0x87, 0x0f,
|
||||
0xa2, 0x0e, 0x5f, 0x7f, 0xd3, 0x03, 0x1b, 0xeb, 0x23, 0xb3, 0xdc, 0xd4, 0xd9, 0x1d, 0x78, 0x3b,
|
||||
0x8b, 0x86, 0xd6, 0x30, 0xee, 0x4b, 0x74, 0x23, 0xdd, 0xe1, 0x0f, 0x2f, 0x7e, 0xdc, 0x1a, 0xdb,
|
||||
0xe0, 0xfe, 0xfb, 0x8f, 0xde, 0x30, 0xca, 0x27, 0xe6, 0x8d, 0xfc, 0x79, 0x0f, 0x00, 0x00, 0xff,
|
||||
0xff, 0xdf, 0x43, 0x89, 0x65, 0x8a, 0x02, 0x00, 0x00,
|
||||
// 395 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0x41, 0x8f, 0x9b, 0x30,
|
||||
0x10, 0x85, 0x05, 0x89, 0x42, 0x18, 0xaa, 0xee, 0xca, 0x87, 0x8a, 0xdd, 0x55, 0x5b, 0xc4, 0xa5,
|
||||
0x39, 0x81, 0x4a, 0xa5, 0xf6, 0xde, 0x2a, 0x87, 0x9c, 0x52, 0x99, 0xaa, 0x87, 0x5e, 0x90, 0x31,
|
||||
0x6e, 0x62, 0x95, 0x60, 0x62, 0x1b, 0x54, 0xd4, 0x7f, 0xd2, 0x5f, 0x5b, 0x61, 0x83, 0xd4, 0x64,
|
||||
0x95, 0xdb, 0xf8, 0xcd, 0x63, 0x78, 0xdf, 0xd8, 0xf0, 0x20, 0x5b, 0x9a, 0x2a, 0x4a, 0x9a, 0x86,
|
||||
0xc9, 0x54, 0x31, 0xd9, 0x73, 0xca, 0x92, 0x56, 0x0a, 0x2d, 0xd0, 0xbd, 0x96, 0xbc, 0x1f, 0x92,
|
||||
0xa9, 0x99, 0xf4, 0xef, 0x1f, 0x3f, 0x1e, 0xb8, 0x3e, 0x76, 0x65, 0x42, 0xc5, 0x29, 0x25, 0xe7,
|
||||
0x8e, 0x28, 0x46, 0x3b, 0xc9, 0xf5, 0x90, 0x1a, 0x67, 0x3a, 0x8e, 0xa2, 0xe2, 0x74, 0x12, 0xcd,
|
||||
0xe5, 0xa4, 0xf8, 0xaf, 0x03, 0x41, 0x4e, 0x49, 0x83, 0xd9, 0xb9, 0x63, 0x4a, 0xa3, 0x57, 0xb0,
|
||||
0xd2, 0x44, 0x1e, 0x98, 0x0e, 0x9d, 0xc8, 0xd9, 0xf8, 0x78, 0x3a, 0xa1, 0xb7, 0x10, 0x10, 0xa9,
|
||||
0xf9, 0x4f, 0x42, 0x75, 0xc1, 0xab, 0xd0, 0x35, 0x4d, 0x98, 0xa5, 0x5d, 0x85, 0x1e, 0x60, 0x5d,
|
||||
0xd6, 0xa2, 0x2c, 0x78, 0xa5, 0xc2, 0x45, 0xb4, 0xd8, 0xf8, 0xd8, 0x1b, 0xcf, 0xbb, 0x4a, 0xa1,
|
||||
0x4f, 0xe0, 0x89, 0x56, 0x73, 0xd1, 0xa8, 0x70, 0x19, 0x39, 0x9b, 0x20, 0x7b, 0x9d, 0x5c, 0xe7,
|
||||
0x4f, 0xc6, 0x0c, 0x7b, 0x6b, 0xc2, 0xb3, 0x3b, 0xce, 0x6d, 0xb6, 0x49, 0x47, 0x4f, 0xe0, 0xf7,
|
||||
0x5d, 0xdd, 0x14, 0x7a, 0x68, 0x59, 0xe8, 0x98, 0x7f, 0xac, 0x47, 0xe1, 0xdb, 0xd0, 0x32, 0xf4,
|
||||
0x0e, 0xee, 0x66, 0xe6, 0x82, 0x1e, 0x19, 0xfd, 0xa5, 0x42, 0xd7, 0x58, 0x5e, 0xce, 0xf2, 0x17,
|
||||
0xa3, 0xc6, 0xbf, 0xe1, 0x85, 0x05, 0x56, 0xad, 0x68, 0x14, 0x43, 0x11, 0xb8, 0x42, 0x19, 0xda,
|
||||
0x20, 0xbb, 0x9f, 0x82, 0xd9, 0x55, 0x25, 0xfb, 0x1c, 0xbb, 0x42, 0x21, 0x04, 0x4b, 0x26, 0x54,
|
||||
0x6d, 0xa0, 0xd7, 0xd8, 0xd4, 0x28, 0x03, 0x4f, 0x32, 0xd5, 0xd5, 0xda, 0xd2, 0x06, 0x59, 0xf8,
|
||||
0x9c, 0x09, 0x1b, 0x03, 0x9e, 0x8d, 0xf1, 0x1f, 0x58, 0x59, 0xe9, 0xe6, 0x96, 0xb7, 0x70, 0x37,
|
||||
0x02, 0x31, 0x49, 0x4a, 0x5e, 0x73, 0xcd, 0x99, 0x85, 0x08, 0xb2, 0xa7, 0xcb, 0x60, 0xdf, 0xff,
|
||||
0x33, 0x0d, 0xf8, 0xfa, 0x9b, 0x31, 0xb0, 0xd9, 0xd1, 0xc2, 0x0c, 0x37, 0x75, 0xf6, 0x15, 0xbc,
|
||||
0xdc, 0x46, 0x43, 0x5b, 0x58, 0x8e, 0x25, 0xba, 0x71, 0x0d, 0xd3, 0x53, 0x78, 0x7c, 0x73, 0xab,
|
||||
0x6d, 0x17, 0xf7, 0xd9, 0xff, 0xe1, 0x4d, 0xad, 0x72, 0x65, 0x1e, 0xd3, 0x87, 0x7f, 0x01, 0x00,
|
||||
0x00, 0xff, 0xff, 0xfd, 0x72, 0xf0, 0x54, 0xb3, 0x02, 0x00, 0x00,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user