diff --git a/go.mod b/go.mod index d745a8f49d..8d52a907d9 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/aquasecurity/table v1.10.0 github.com/aquasecurity/testdocker v0.0.0-20240730042311-4642e94c7fc8 github.com/aquasecurity/tml v0.6.1 - github.com/aquasecurity/trivy-checks v1.11.2-0.20250529074512-7afea1b738c4 + github.com/aquasecurity/trivy-checks v1.11.3-0.20250604022615-9a7efa7c9169 github.com/aquasecurity/trivy-db v0.0.0-20250529093513-a12dfc204b6e github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 github.com/aquasecurity/trivy-kubernetes v0.9.0 diff --git a/go.sum b/go.sum index c3b93de014..bc0c967340 100644 --- a/go.sum +++ b/go.sum @@ -800,8 +800,8 @@ github.com/aquasecurity/testdocker v0.0.0-20240730042311-4642e94c7fc8 h1:b43UVqY github.com/aquasecurity/testdocker v0.0.0-20240730042311-4642e94c7fc8/go.mod h1:wXA9k3uuaxY3yu7gxrxZDPo/04FEMJtwyecdAlYrEIo= github.com/aquasecurity/tml v0.6.1 h1:y2ZlGSfrhnn7t4ZJ/0rotuH+v5Jgv6BDDO5jB6A9gwo= github.com/aquasecurity/tml v0.6.1/go.mod h1:OnYMWY5lvI9ejU7yH9LCberWaaTBW7hBFsITiIMY2yY= -github.com/aquasecurity/trivy-checks v1.11.2-0.20250529074512-7afea1b738c4 h1:Njp9YEU+4vqmtcb21lWfivrbiLsdYreohmWQX3+KHiU= -github.com/aquasecurity/trivy-checks v1.11.2-0.20250529074512-7afea1b738c4/go.mod h1:nT69xgRcBD4NlHwTBpWMYirpK5/Zpl8M+XDOgmjMn2k= +github.com/aquasecurity/trivy-checks v1.11.3-0.20250604022615-9a7efa7c9169 h1:TckzIxUX7lZaU9f2lNxCN0noYYP8fzmSQf6a4JdV83w= +github.com/aquasecurity/trivy-checks v1.11.3-0.20250604022615-9a7efa7c9169/go.mod h1:nT69xgRcBD4NlHwTBpWMYirpK5/Zpl8M+XDOgmjMn2k= github.com/aquasecurity/trivy-db v0.0.0-20250529093513-a12dfc204b6e h1:+B/in1DQDGwQbKhW5pWL8XxBgnZKxXhUznylJ2NCyvs= github.com/aquasecurity/trivy-db v0.0.0-20250529093513-a12dfc204b6e/go.mod h1:4zd4qZcjhNAHASz5I0O7qapv5h5gSJzSEaZXv/IPOGc= github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 h1:JVgBIuIYbwG+ekC5lUHUpGJboPYiCcxiz06RCtz8neI= diff --git a/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go b/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go index 6dea2cd81e..ae88501374 100644 --- a/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go +++ b/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "regexp" "strings" v1 "github.com/google/go-containerregistry/pkg/v1" @@ -87,6 +88,9 @@ func imageConfigToDockerfile(cfg *v1.ConfigFile) []byte { case strings.HasPrefix(h.CreatedBy, "/bin/sh -c #(nop)"): // Instruction other than RUN createdBy = strings.TrimPrefix(h.CreatedBy, "/bin/sh -c #(nop)") + if strings.HasPrefix(createdBy, " COPY") || strings.HasPrefix(createdBy, " ADD") { + createdBy = normalizeCopyCreatedBy(createdBy) + } case strings.HasPrefix(h.CreatedBy, "/bin/sh -c"): // RUN instruction createdBy = buildRunInstruction(createdBy) @@ -112,6 +116,9 @@ func imageConfigToDockerfile(cfg *v1.ConfigFile) []byte { } } } + // Remove Buildah-specific suffix (currently only `|inherit Labels=false`) + // cf. https://github.com/containers/buildah/blob/5a02e74b5d0f01e4d68ea0dcdbf5f5f444baa68f/imagebuildah/stage_executor.go#L1885 + createdBy = strings.TrimSuffix(createdBy, "|inheritLabels=false") dockerfile.WriteString(strings.TrimSpace(createdBy) + "\n") } @@ -150,6 +157,12 @@ func buildHealthcheckInstruction(health *v1.HealthConfig) string { return fmt.Sprintf("HEALTHCHECK %s%s%s%s%s", interval, timeout, startPeriod, retries, command) } +var copyInRe = regexp.MustCompile(`\b((?:file|dir):\S+) in `) + +func normalizeCopyCreatedBy(input string) string { + return copyInRe.ReplaceAllString(input, `$1 `) +} + func (a *historyAnalyzer) Required(_ types.OS) bool { return true } diff --git a/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile_test.go b/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile_test.go index 044d428010..f0e7c78de5 100644 --- a/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile_test.go +++ b/pkg/fanal/analyzer/imgconf/dockerfile/dockerfile_test.go @@ -73,7 +73,7 @@ func Test_historyAnalyzer_Analyze(t *testing.T) { }, History: []v1.History{ { - CreatedBy: "/bin/sh -c #(nop) ADD file:e4d600fc4c9c293efe360be7b30ee96579925d1b4634c94332e2ec73f7d8eca1 /", + CreatedBy: "/bin/sh -c #(nop) ADD foo.txt /", EmptyLayer: false, }, { @@ -99,7 +99,7 @@ func Test_historyAnalyzer_Analyze(t *testing.T) { types.MisconfResult{ Namespace: "builtin.dockerfile.DS005", Query: "data.builtin.dockerfile.DS005.deny", - Message: "Consider using 'COPY file:e4d600fc4c9c293efe360be7b30ee96579925d1b4634c94332e2ec73f7d8eca1 /' command instead of 'ADD file:e4d600fc4c9c293efe360be7b30ee96579925d1b4634c94332e2ec73f7d8eca1 /'", + Message: "Consider using 'COPY foo.txt /' command instead of 'ADD foo.txt /'", PolicyMetadata: types.PolicyMetadata{ ID: "DS005", AVDID: "AVD-DS-0005", @@ -119,10 +119,10 @@ func Test_historyAnalyzer_Analyze(t *testing.T) { Lines: []types.Line{ { Number: 1, - Content: "ADD file:e4d600fc4c9c293efe360be7b30ee96579925d1b4634c94332e2ec73f7d8eca1 /", + Content: "ADD foo.txt /", IsCause: true, Truncated: false, - Highlighted: "\x1b[38;5;64mADD\x1b[0m file:e4d600fc4c9c293efe360be7b30ee96579925d1b4634c94332e2ec73f7d8eca1 /", + Highlighted: "\x1b[38;5;64mADD\x1b[0m foo.txt /", FirstCause: true, LastCause: true, }, @@ -428,6 +428,26 @@ func Test_ImageConfigToDockerfile(t *testing.T) { expected: `ARG TAG=latest ENV TAG=latest ENTRYPOINT ["/bin/sh" "-c" "echo test"] +`, + }, + { + name: "buildah backend or docker legacy builder (DOCKER_BUILDKIT=0)", + input: &v1.ConfigFile{ + History: []v1.History{ + { + CreatedBy: "/bin/sh -c #(nop) COPY dir:3a024d8085bc39741a0a094a8e287a00a760975c7c2e6b5dc6c7d3174b7d1ab6 in ./files |inheritLabels=false", + }, + { + CreatedBy: "/bin/sh -c #(nop) ADD file:24d346633efc860b5011cefa5c0af73006e74e5dfb3c5c0e9cb0e90a927931e1 in readme |inheritLabels=false", + }, + { + CreatedBy: `/bin/sh -c #(nop) ENTRYPOINT ["/bin/sh"]|inheritLabels=false`, + }, + }, + }, + expected: `COPY dir:3a024d8085bc39741a0a094a8e287a00a760975c7c2e6b5dc6c7d3174b7d1ab6 ./files +ADD file:24d346633efc860b5011cefa5c0af73006e74e5dfb3c5c0e9cb0e90a927931e1 readme +ENTRYPOINT ["/bin/sh"] `, }, }