Compare commits

...

14 Commits

Author SHA1 Message Date
Teppei Fukuda
85e45cad95 chore(ci): change to more granular tokens (#1014) 2021-05-24 02:35:44 +03:00
rahul2393
9fa512a652 chore(ci): add Go scanning and update dependencies (#1001)
* Fixed dependency

* Scan repo action added

* only check for Critical with exit-code=1

* Fix vulnerability in go.sum

* more fixes
2021-05-23 11:21:23 +03:00
skovati
349371bbc9 docs: Add HIGH severity to Trivy command in GitLab CI example to match comment (#1013)
* Add HIGH severity to Trivy command in GitLab CI example to match comment

* Change comment to accurately reflect Trivy command that fails only on CRITICAL vulnerabilities
2021-05-23 04:55:46 +03:00
Teppei Fukuda
4446961167 fix(image): disable go.sum scanning (#1007) 2021-05-20 10:01:19 +03:00
Teppei Fukuda
04473ada48 fix(gomod): handle go.sum with an empty line (#1006) 2021-05-20 09:39:06 +03:00
Teppei Fukuda
1b66b77f69 feat: prepare for config scanning (#1005)
* temp: disable config scanning
2021-05-20 09:05:36 +03:00
gboer
8fc6ea6489 Clarify that dev dependencies are excluded (#986)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
2021-05-19 05:47:05 +03:00
rahul2393
eaf2da20a6 Include target value in Sarif template ruleID (#991)
* Include target value in Sarif template ruleID

* Fix format
2021-05-13 20:10:34 +03:00
rahul2393
083c157b05 chore(mkdocs): allow workflow_dispatch (#989)
* Updated docs for go libraries.

* Add option to manually trigger doc publish
2021-05-12 19:57:14 +03:00
rahul2393
e26e39a7f8 fix(vuln) unique vulnerabilities from different data sources (#984)
* Fix duplicate Rule in sarif template

* Fix integration tests

* Fixed tests

* Update certs validity upto 2100

* Moved deduplication logic to Filter

* Fix linting issue

* Fix liniting issue

* fix: deduplicate vulnerabilities

* refactor

* fix: add installed versions to uniq keys

* Fix tests

* Fix Unit tests.

* Revert port change

Co-authored-by: knqyf263 <knqyf263@gmail.com>
2021-05-12 13:10:05 +03:00
rahul2393
04e7ccabea feat(go): added support of gomod analyzer (#978)
* Added support of gomod analyzer.

* add imports

* fix gocyclo

* Ran go fmt
2021-05-11 04:18:13 +03:00
rahul2393
415e1d8ea3 Upgrade fanal dependency (#976) 2021-05-01 13:14:08 +03:00
Teppei Fukuda
3bb8852ef7 docs: mention upx binaries (#974) 2021-05-01 04:39:42 +03:00
rahul2393
c0fddd9467 Upgrade alpine to fix git and libcurl vulnerabilities in trivy docker image scan (#971)
* Upgrade alpine to fix git and libcurl vulnerabilities in trivy docker image scan

* remove patch version
2021-04-30 11:06:57 +03:00
90 changed files with 2513 additions and 2435 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,7 +15,7 @@ $(GOBIN)/wire:
.PHONY: wire
wire: $(GOBIN)/wire
wire gen ./pkg/... ./internal/...
wire gen ./pkg/...
.PHONY: mock
mock: $(GOBIN)/mockery

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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://") &&

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

@@ -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
View 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
View 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
View 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
View 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
View 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
View 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 &#34;DARN&#34; 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 &lt; 7.20.0 and curl &gt;= 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 &#39;easy&#39; 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)
})
}
}

View File

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

View File

@@ -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 &#34;DARN&#34; 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 &lt; 7.20.0 and curl &gt;= 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 &#39;easy&#39; 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)
})
}
}

View File

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

View File

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

View File

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

View File

@@ -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: &timestamp.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)

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,3 +1,6 @@
# test
# vulnerabilities
CVE-2019-0001
CVE-2019-0002
# misconfigurations
ID100

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

View 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

View File

@@ -0,0 +1,6 @@
- bucket: vulnerability
pairs:
- key: CVE-2019-0001
value:
Title:
- aaa

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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