diff --git a/.github/workflows/release-pr-check.yaml b/.github/workflows/release-pr-check.yaml new file mode 100644 index 000000000000..a9662b1879ce --- /dev/null +++ b/.github/workflows/release-pr-check.yaml @@ -0,0 +1,19 @@ +name: Backport PR Check + +on: + pull_request: + branches: + - 'release/v*' + +jobs: + check-pr-author: + runs-on: ubuntu-latest + steps: + - name: Check PR author + id: check_author + run: | + if [ "${{ github.actor }}" != "aqua-bot" ]; then + echo "::error::This branch is intended for automated backporting by bot. Please refer to the documentation:" + echo "::error::https://trivy.dev/latest/community/maintainer/backporting/" + exit 1 + fi \ No newline at end of file diff --git a/contrib/junit.tpl b/contrib/junit.tpl index 27b654b7b049..b351f62bb43e 100644 --- a/contrib/junit.tpl +++ b/contrib/junit.tpl @@ -44,5 +44,15 @@ {{- end }} +{{- if .Secrets }} + {{- $secrets := len .Secrets }} + {{ range .Secrets }} + + {{ escapeXML .Match }} + + {{- end }} + +{{- end }} + {{- end }} diff --git a/docs/docs/configuration/reporting.md b/docs/docs/configuration/reporting.md index c515753e9d17..c6ddc1727a71 100644 --- a/docs/docs/configuration/reporting.md +++ b/docs/docs/configuration/reporting.md @@ -415,8 +415,8 @@ If Trivy is installed using rpm then default templates can be found at `/usr/loc |:----------------:|:---------:| | Vulnerability | ✓ | | Misconfiguration | ✓ | -| Secret | | -| License | | +| Secret | ✓ | +| License | ✓ | In the following example using the template `junit.tpl` XML can be generated. ``` diff --git a/docs/docs/coverage/os/index.md b/docs/docs/coverage/os/index.md index 8756ddc8aa95..74a40787c600 100644 --- a/docs/docs/coverage/os/index.md +++ b/docs/docs/coverage/os/index.md @@ -11,7 +11,7 @@ Trivy supports operating systems for | OS | Supported Versions | Package Managers | |---------------------------------------|-------------------------------------|------------------| -| [Alpine Linux](alpine.md) | 2.2 - 2.7, 3.0 - 3.20, edge | apk | +| [Alpine Linux](alpine.md) | 2.2 - 2.7, 3.0 - 3.21, edge | apk | | [Wolfi Linux](wolfi.md) | (n/a) | apk | | [Chainguard](chainguard.md) | (n/a) | apk | | [Red Hat Enterprise Linux](rhel.md) | 6, 7, 8 | dnf/yum/rpm | diff --git a/go.mod b/go.mod index 35820ac7f036..abeb35bfa868 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,7 @@ require ( github.com/docker/docker v27.4.1+incompatible github.com/docker/go-connections v0.5.0 github.com/fatih/color v1.18.0 - github.com/go-git/go-git/v5 v5.12.0 + github.com/go-git/go-git/v5 v5.13.1 github.com/go-openapi/runtime v0.28.0 // indirect github.com/go-openapi/strfmt v0.23.0 // indirect github.com/go-redis/redis/v8 v8.11.5 @@ -165,7 +165,7 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.12.9 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect - github.com/ProtonMail/go-crypto v1.1.2 // indirect + github.com/ProtonMail/go-crypto v1.1.3 // indirect github.com/VividCortex/ewma v1.2.0 // indirect github.com/agext/levenshtein v1.2.3 // indirect github.com/agnivade/levenshtein v1.2.0 // indirect @@ -206,7 +206,7 @@ require ( github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 // indirect - github.com/cyphar/filepath-securejoin v0.3.4 // indirect + github.com/cyphar/filepath-securejoin v0.3.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect @@ -229,7 +229,7 @@ require ( github.com/go-chi/chi v4.1.2+incompatible // indirect github.com/go-errors/errors v1.4.2 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect - github.com/go-git/go-billy/v5 v5.6.0 // indirect + github.com/go-git/go-billy/v5 v5.6.1 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/go-jose/go-jose/v4 v4.0.4 // indirect @@ -351,7 +351,7 @@ require ( github.com/sigstore/cosign/v2 v2.2.4 // indirect github.com/sigstore/sigstore v1.8.10 // indirect github.com/sigstore/timestamp-authority v1.2.2 // indirect - github.com/skeema/knownhosts v1.2.2 // indirect + github.com/skeema/knownhosts v1.3.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/stretchr/objx v0.5.2 // indirect diff --git a/go.sum b/go.sum index eab909b888e7..6d7868d7f729 100644 --- a/go.sum +++ b/go.sum @@ -283,8 +283,8 @@ github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMo github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= -github.com/ProtonMail/go-crypto v1.1.2 h1:A7JbD57ThNqh7XjmHE+PXpQ3Dqt3BrSAC0AL0Go3KS0= -github.com/ProtonMail/go-crypto v1.1.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk= +github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/ThalesIgnite/crypto11 v1.2.5 h1:1IiIIEqYmBvUYFeMnHqRft4bwf/O36jryEUpY+9ef8E= @@ -525,8 +525,8 @@ github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0= github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46 h1:2Dx4IHfC1yHWI12AxQDJM1QbRCDfk6M+blLzlZCXdrc= github.com/cyberphone/json-canonicalization v0.0.0-20231011164504-785e29786b46/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= -github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8= -github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM= +github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM= +github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8GQs= github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -577,8 +577,8 @@ github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5Jflh github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= -github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/elazarl/goproxy v1.2.3 h1:xwIyKHbaP5yfT6O9KIeYJR5549MXRQkoQMRXGztz8YQ= +github.com/elazarl/goproxy v1.2.3/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64= github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/proto v1.12.1 h1:6n/Z2pZAnBwuhU66Gs8160B8rrrYKo7h2F2sCOnNceE= @@ -624,20 +624,20 @@ github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcP github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4= github.com/glebarez/go-sqlite v1.20.3/go.mod h1:u3N6D/wftiAzIOJtZl6BmedqxmmkDfH3q+ihjqxC9u0= -github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE= -github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8= -github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM= +github.com/go-git/go-billy/v5 v5.6.1 h1:u+dcrgaguSSkbjzHwelEjc0Yj300NUevrrPphk/SoRA= +github.com/go-git/go-billy/v5 v5.6.1/go.mod h1:0AsLr1z2+Uksi4NlElmMblP5rPcDZNRCD8ujZCRR2BE= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys= -github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY= +github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M= +github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -1297,8 +1297,8 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A= -github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo= +github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= +github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/sosedoff/gitkit v0.4.0 h1:opyQJ/h9xMRLsz2ca/2CRXtstePcpldiZN8DpLLF8Os= diff --git a/goreleaser-canary.yml b/goreleaser-canary.yml index b49abde49d34..ed360afbdd8d 100644 --- a/goreleaser-canary.yml +++ b/goreleaser-canary.yml @@ -3,7 +3,7 @@ version: 2 project_name: trivy_canary_build builds: - - main: cmd/trivy/main.go + main: ./cmd/trivy/ binary: trivy ldflags: - -s -w diff --git a/helm/trivy/Chart.yaml b/helm/trivy/Chart.yaml index 505881ef8c93..e2cf46ff51d0 100644 --- a/helm/trivy/Chart.yaml +++ b/helm/trivy/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: trivy -version: 0.10.0 -appVersion: 0.58.0 +version: 0.10.1 +appVersion: 0.58.1 description: Trivy helm chart keywords: - scanner diff --git a/pkg/compliance/spec/compliance.go b/pkg/compliance/spec/compliance.go index 0d9b4cde810c..794d244ca9b4 100644 --- a/pkg/compliance/spec/compliance.go +++ b/pkg/compliance/spec/compliance.go @@ -9,7 +9,7 @@ import ( "golang.org/x/xerrors" "gopkg.in/yaml.v3" - sp "github.com/aquasecurity/trivy-checks/pkg/spec" + "github.com/aquasecurity/trivy-checks/pkg/specs" iacTypes "github.com/aquasecurity/trivy/pkg/iac/types" "github.com/aquasecurity/trivy/pkg/log" "github.com/aquasecurity/trivy/pkg/set" @@ -97,7 +97,7 @@ func GetComplianceSpec(specNameOrPath, cacheDir string) (ComplianceSpec, error) } else { _, err := os.Stat(filepath.Join(checksDir(cacheDir), "metadata.json")) if err != nil { // cache corrupt or bundle does not exist, load embedded version - b = []byte(sp.NewSpecLoader().GetSpecByName(specNameOrPath)) + b = []byte(specs.GetSpec(specNameOrPath)) log.Debug("Compliance spec loaded from embedded library", log.String("spec", specNameOrPath)) } else { // load from bundle on disk diff --git a/pkg/detector/ospkg/alpine/alpine.go b/pkg/detector/ospkg/alpine/alpine.go index 063c3018f3f1..a3baa8571aa3 100644 --- a/pkg/detector/ospkg/alpine/alpine.go +++ b/pkg/detector/ospkg/alpine/alpine.go @@ -48,6 +48,7 @@ var ( "3.18": time.Date(2025, 5, 9, 23, 59, 59, 0, time.UTC), "3.19": time.Date(2025, 11, 1, 23, 59, 59, 0, time.UTC), "3.20": time.Date(2026, 04, 1, 23, 59, 59, 0, time.UTC), + "3.21": time.Date(2026, 12, 5, 23, 59, 59, 0, time.UTC), "edge": time.Date(9999, 1, 1, 0, 0, 0, 0, time.UTC), } ) diff --git a/pkg/fanal/analyzer/buildinfo/content_manifest.go b/pkg/fanal/analyzer/buildinfo/content_manifest.go index 7d5372dc7266..ccb4b175a645 100644 --- a/pkg/fanal/analyzer/buildinfo/content_manifest.go +++ b/pkg/fanal/analyzer/buildinfo/content_manifest.go @@ -10,12 +10,18 @@ import ( "github.com/aquasecurity/trivy/pkg/fanal/analyzer" "github.com/aquasecurity/trivy/pkg/fanal/types" + "github.com/aquasecurity/trivy/pkg/set" ) func init() { analyzer.RegisterAnalyzer(&contentManifestAnalyzer{}) } +var contentSetsDirs = set.New[string]( + "root/buildinfo/content_manifests/", + "usr/share/buildinfo/", // for RHCOS +) + const contentManifestAnalyzerVersion = 1 type contentManifest struct { @@ -44,7 +50,7 @@ func (a contentManifestAnalyzer) Analyze(_ context.Context, target analyzer.Anal func (a contentManifestAnalyzer) Required(filePath string, _ os.FileInfo) bool { dir, file := filepath.Split(filepath.ToSlash(filePath)) - if dir != "root/buildinfo/content_manifests/" { + if !contentSetsDirs.Contains(dir) { return false } return filepath.Ext(file) == ".json" diff --git a/pkg/fanal/analyzer/buildinfo/content_manifest_test.go b/pkg/fanal/analyzer/buildinfo/content_manifest_test.go index 61ad8ebde1cb..4ef87bebbeb6 100644 --- a/pkg/fanal/analyzer/buildinfo/content_manifest_test.go +++ b/pkg/fanal/analyzer/buildinfo/content_manifest_test.go @@ -73,12 +73,22 @@ func Test_contentManifestAnalyzer_Required(t *testing.T) { want bool }{ { - name: "happy path", + name: "happy path root dir", filePath: "root/buildinfo/content_manifests/nodejs-12-container-1-66.json", want: true, }, { - name: "sad path", + name: "happy path usr dir", + filePath: "usr/share/buildinfo/nodejs-12-container-1-66.json", + want: true, + }, + { + name: "sad path wrong dir", + filePath: "foo/bar/nodejs-12-container-1-66.json", + want: false, + }, + { + name: "sad path wrong extension", filePath: "root/buildinfo/content_manifests/nodejs-12-container-1-66.xml", want: false, }, diff --git a/pkg/fanal/applier/applier_test.go b/pkg/fanal/applier/applier_test.go index b2a992f80012..baf34975d778 100644 --- a/pkg/fanal/applier/applier_test.go +++ b/pkg/fanal/applier/applier_test.go @@ -961,6 +961,91 @@ func TestApplier_ApplyLayers(t *testing.T) { }, wantErr: "unknown OS", }, + { + name: "SUSE images - legacy OS name with backward compatibility", + args: args{ + imageID: "sha256:fb44d01953611ba18d43d88e158c25579d18eff42db671182245010620a283f3", + layerIDs: []string{ + "sha256:2615f175cf3da67c48c6542914744943ee5e9c253547b03e3cfe8aae605c3199", + }, + }, + getLayerExpectations: []cache.LocalArtifactCacheGetBlobExpectation{ + { + Args: cache.LocalArtifactCacheGetBlobArgs{ + BlobID: "sha256:2615f175cf3da67c48c6542914744943ee5e9c253547b03e3cfe8aae605c3199", + }, + Returns: cache.LocalArtifactCacheGetBlobReturns{ + BlobInfo: types.BlobInfo{ + SchemaVersion: 1, + Digest: "sha256:fb44d01953611ba18d43d88e158c25579d18eff42db671182245010620a283f3", + DiffID: "sha256:d555e1b0b42f21a1cf198e52bcb12fe66aa015348e4390d2d5acddd327d79073", + OS: types.OS{ + Family: "suse linux enterprise server", + Name: "15.4", + }, + PackageInfos: []types.PackageInfo{ + { + FilePath: "usr/lib/sysimage/rpm/Packages.db", + Packages: types.Packages{ + { + Name: "curl", + Version: "7.79.1", + SrcName: "curl", + SrcVersion: "7.79.1", + }, + }, + }, + }, + }, + }, + }, + }, + getArtifactExpectations: []cache.LocalArtifactCacheGetArtifactExpectation{ + { + Args: cache.LocalArtifactCacheGetArtifactArgs{ + ArtifactID: "sha256:fb44d01953611ba18d43d88e158c25579d18eff42db671182245010620a283f3", + }, + Returns: cache.LocalArtifactCacheGetArtifactReturns{ + ArtifactInfo: types.ArtifactInfo{ + SchemaVersion: 1, + }, + }, + }, + }, + want: types.ArtifactDetail{ + OS: types.OS{ + Family: "sles", + Name: "15.4", + }, + Packages: types.Packages{ + { + Name: "curl", + Version: "7.79.1", + SrcName: "curl", + SrcVersion: "7.79.1", + Identifier: types.PkgIdentifier{ + UID: "1e9b3d3a73785651", + PURL: &packageurl.PackageURL{ + Type: packageurl.TypeRPM, + Namespace: "suse", + Name: "curl", + Version: "7.79.1", + Qualifiers: packageurl.Qualifiers{ + { + Key: "distro", + Value: "sles-15.4", + }, + }, + }, + }, + Layer: types.Layer{ + Digest: "sha256:fb44d01953611ba18d43d88e158c25579d18eff42db671182245010620a283f3", + DiffID: "sha256:d555e1b0b42f21a1cf198e52bcb12fe66aa015348e4390d2d5acddd327d79073", + }, + }, + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/fanal/types/artifact.go b/pkg/fanal/types/artifact.go index c246b8dfbde8..a3f861d4e910 100644 --- a/pkg/fanal/types/artifact.go +++ b/pkg/fanal/types/artifact.go @@ -27,6 +27,13 @@ func (o *OS) Detected() bool { return o.Family != "" } +// Normalize normalizes OS family names for backward compatibility +func (o *OS) Normalize() { + if alias, ok := OSTypeAliases[o.Family]; ok { + o.Family = alias + } +} + // Merge merges OS version and enhanced security maintenance programs func (o *OS) Merge(newOS OS) { if lo.IsEmpty(newOS) { @@ -53,6 +60,11 @@ func (o *OS) Merge(newOS OS) { o.Extended = true } } + // When merging layers, there are cases when a layer contains an OS with an old name: + // - Cache contains a layer derived from an old version of Trivy. + // - `client` uses an old version of Trivy, but `server` is a new version of Trivy (for `client/server` mode). + // So we need to normalize the OS name for backward compatibility. + o.Normalize() } type Repository struct { diff --git a/pkg/fanal/types/const.go b/pkg/fanal/types/const.go index 6b5fcadccffe..d7e0a4ec0a68 100644 --- a/pkg/fanal/types/const.go +++ b/pkg/fanal/types/const.go @@ -43,6 +43,15 @@ const ( Wolfi OSType = "wolfi" ) +// OSTypeAliases is a map of aliases for operating systems. +// This is used to map the old family names to the new ones for backward compatibility. +var OSTypeAliases = map[OSType]OSType{ + "opensuse.leap": OpenSUSELeap, + "opensuse.tumbleweed": OpenSUSETumbleweed, + "suse linux enterprise micro": SLEMicro, + "suse linux enterprise server": SLES, +} + // Programming language dependencies const ( Bundler LangType = "bundler" diff --git a/pkg/iac/scanners/helm/scanner.go b/pkg/iac/scanners/helm/scanner.go index 6891606d908a..480d2b2edd46 100644 --- a/pkg/iac/scanners/helm/scanner.go +++ b/pkg/iac/scanners/helm/scanner.go @@ -12,6 +12,7 @@ import ( "github.com/liamg/memoryfs" "github.com/aquasecurity/trivy/pkg/iac/detection" + "github.com/aquasecurity/trivy/pkg/iac/ignore" "github.com/aquasecurity/trivy/pkg/iac/rego" "github.com/aquasecurity/trivy/pkg/iac/scan" "github.com/aquasecurity/trivy/pkg/iac/scanners" @@ -125,6 +126,7 @@ func (s *Scanner) getScanResults(path string, ctx context.Context, target fs.FS) file := file s.logger.Debug("Processing rendered chart file", log.FilePath(file.TemplateFilePath)) + ignoreRules := ignore.Parse(file.ManifestContent, file.TemplateFilePath, "") manifests, err := kparser.Parse(ctx, strings.NewReader(file.ManifestContent), file.TemplateFilePath) if err != nil { return nil, fmt.Errorf("unmarshal yaml: %w", err) @@ -150,6 +152,7 @@ func (s *Scanner) getScanResults(path string, ctx context.Context, target fs.FS) return nil, err } fileResults.SetSourceAndFilesystem(helmParser.ChartSource, renderedFS, detection.IsArchive(helmParser.ChartSource)) + fileResults.Ignore(ignoreRules, nil) } results = append(results, fileResults...) diff --git a/pkg/iac/scanners/helm/test/scanner_test.go b/pkg/iac/scanners/helm/test/scanner_test.go index ef751ac2a7b8..8cb0c7e39e27 100644 --- a/pkg/iac/scanners/helm/test/scanner_test.go +++ b/pkg/iac/scanners/helm/test/scanner_test.go @@ -10,6 +10,7 @@ import ( "strings" "testing" + "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -136,28 +137,28 @@ func Test_helm_scanner_with_dir(t *testing.T) { require.NotNil(t, results) failed := results.GetFailed() - assert.Len(t, failed, 14) + assert.Len(t, failed, 13) visited := make(map[string]bool) - var errorCodes []string for _, result := range failed { - id := result.Flatten().RuleID - if _, exists := visited[id]; !exists { - visited[id] = true - errorCodes = append(errorCodes, id) - } + visited[result.Rule().AVDID] = true } + errorCodes := lo.Keys(visited) - sort.Strings(errorCodes) - - assert.Equal(t, []string{ + assert.ElementsMatch(t, []string{ "AVD-KSV-0001", "AVD-KSV-0003", "AVD-KSV-0011", "AVD-KSV-0012", "AVD-KSV-0014", - "AVD-KSV-0015", "AVD-KSV-0016", "AVD-KSV-0018", + "AVD-KSV-0015", "AVD-KSV-0016", "AVD-KSV-0020", "AVD-KSV-0021", "AVD-KSV-0030", "AVD-KSV-0104", "AVD-KSV-0106", "AVD-KSV-0117", }, errorCodes) + + ignored := results.GetIgnored() + assert.Len(t, ignored, 1) + + assert.Equal(t, "AVD-KSV-0018", ignored[0].Rule().AVDID) + assert.Equal(t, "templates/deployment.yaml", ignored[0].Metadata().Range().GetFilename()) } } @@ -231,19 +232,12 @@ deny[res] { assert.Len(t, failed, 15) visited := make(map[string]bool) - var errorCodes []string for _, result := range failed { - id := result.Flatten().RuleID - if _, exists := visited[id]; !exists { - visited[id] = true - errorCodes = append(errorCodes, id) - } + visited[result.Rule().AVDID] = true } - assert.Len(t, errorCodes, 14) - - sort.Strings(errorCodes) + errorCodes := lo.Keys(visited) - assert.Equal(t, []string{ + assert.ElementsMatch(t, []string{ "AVD-KSV-0001", "AVD-KSV-0003", "AVD-KSV-0011", "AVD-KSV-0012", "AVD-KSV-0014", "AVD-KSV-0015", "AVD-KSV-0016", "AVD-KSV-0018", diff --git a/pkg/iac/scanners/helm/test/testdata/expected/options/testchart/templates/deployment.yaml b/pkg/iac/scanners/helm/test/testdata/expected/options/testchart/templates/deployment.yaml index c41133c72716..1280e673b92a 100644 --- a/pkg/iac/scanners/helm/test/testdata/expected/options/testchart/templates/deployment.yaml +++ b/pkg/iac/scanners/helm/test/testdata/expected/options/testchart/templates/deployment.yaml @@ -25,6 +25,7 @@ spec: securityContext: {} containers: + # trivy:ignore:KSV018 - name: testchart securityContext: runAsUser: 0 diff --git a/pkg/iac/scanners/helm/test/testdata/expected/testchart/templates/deployment.yaml b/pkg/iac/scanners/helm/test/testdata/expected/testchart/templates/deployment.yaml index 8ace433f0c03..9a2e277c106a 100644 --- a/pkg/iac/scanners/helm/test/testdata/expected/testchart/templates/deployment.yaml +++ b/pkg/iac/scanners/helm/test/testdata/expected/testchart/templates/deployment.yaml @@ -25,6 +25,7 @@ spec: securityContext: {} containers: + # trivy:ignore:KSV018 - name: testchart securityContext: {} diff --git a/pkg/iac/scanners/helm/test/testdata/testchart/templates/deployment.yaml b/pkg/iac/scanners/helm/test/testdata/testchart/templates/deployment.yaml index cde22bc4fcc5..4f81b4f85eb9 100644 --- a/pkg/iac/scanners/helm/test/testdata/testchart/templates/deployment.yaml +++ b/pkg/iac/scanners/helm/test/testdata/testchart/templates/deployment.yaml @@ -28,6 +28,7 @@ spec: securityContext: {{- toYaml .Values.podSecurityContext | nindent 8 }} containers: + # trivy:ignore:KSV018 - name: {{ .Chart.Name }} securityContext: {{- toYaml .Values.securityContext | nindent 12 }} diff --git a/pkg/iac/scanners/terraform/parser/load_module.go b/pkg/iac/scanners/terraform/parser/load_module.go index 595e1b02c33e..878bf075baec 100644 --- a/pkg/iac/scanners/terraform/parser/load_module.go +++ b/pkg/iac/scanners/terraform/parser/load_module.go @@ -25,7 +25,7 @@ type ModuleDefinition struct { } func (d *ModuleDefinition) inputVars() map[string]cty.Value { - inputs := d.Definition.Values().AsValueMap() + inputs := d.Definition.NullableValues().AsValueMap() if inputs == nil { return make(map[string]cty.Value) } diff --git a/pkg/iac/scanners/terraform/parser/parser_test.go b/pkg/iac/scanners/terraform/parser/parser_test.go index e88dd017d2fa..e7ffa3230be3 100644 --- a/pkg/iac/scanners/terraform/parser/parser_test.go +++ b/pkg/iac/scanners/terraform/parser/parser_test.go @@ -2161,3 +2161,29 @@ resource "foo" "this" { }) } } + +func TestAttrRefToNullVariable(t *testing.T) { + fsys := fstest.MapFS{ + "main.tf": &fstest.MapFile{Data: []byte(`variable "name" { + type = string + default = null +} + +resource "aws_s3_bucket" "example" { + bucket = var.name +}`)}, + } + + parser := New(fsys, "", OptionStopOnHCLError(true)) + + require.NoError(t, parser.ParseFS(context.TODO(), ".")) + + _, err := parser.Load(context.TODO()) + require.NoError(t, err) + + modules, _, err := parser.EvaluateAll(context.TODO()) + require.NoError(t, err) + + val := modules.GetResourcesByType("aws_s3_bucket")[0].GetAttribute("bucket").GetRawValue() + assert.Nil(t, val) +} diff --git a/pkg/iac/terraform/block.go b/pkg/iac/terraform/block.go index d57d4331c1c4..4e9f794e914c 100644 --- a/pkg/iac/terraform/block.go +++ b/pkg/iac/terraform/block.go @@ -569,13 +569,25 @@ func (b *Block) Attributes() map[string]*Attribute { return attributes } +func (b *Block) NullableValues() cty.Value { + return b.values(true) +} + func (b *Block) Values() cty.Value { + return b.values(false) +} + +func (b *Block) values(allowNull bool) cty.Value { values := createPresetValues(b) for _, attribute := range b.GetAttributes() { if attribute.Name() == "for_each" { continue } - values[attribute.Name()] = attribute.NullableValue() + if allowNull { + values[attribute.Name()] = attribute.NullableValue() + } else { + values[attribute.Name()] = attribute.Value() + } } return cty.ObjectVal(postProcessValues(b, values)) }