mirror of
https://github.com/moby/buildkit.git
synced 2026-08-07 08:10:44 +00:00
provenance: ensure URLs are redacted before written
HTTP and Git URLs may contain inlined credentials in some cases. Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
@@ -52,7 +52,7 @@ func testBuildInfoSources(t *testing.T, sb integration.Sandbox) {
|
||||
dockerfile := `
|
||||
FROM alpine:latest@sha256:21a3deaa0d32a8057914f36584b5288d2e5ecc984380bc0118285c70fa8c9300 AS alpine
|
||||
FROM busybox:latest
|
||||
ADD https://raw.githubusercontent.com/moby/moby/v20.10.21/README.md /
|
||||
ADD https://user2:pw2@raw.githubusercontent.com/moby/moby/v20.10.21/README.md /
|
||||
COPY --from=alpine /bin/busybox /alpine-busybox
|
||||
`
|
||||
|
||||
@@ -93,10 +93,15 @@ COPY --from=alpine /bin/busybox /alpine-busybox
|
||||
}}
|
||||
}
|
||||
|
||||
expectedURL := strings.Replace(server.URL, "http://", "http://xxxxx:xxxxx@", 1)
|
||||
require.NotEqual(t, expectedURL, server.URL)
|
||||
server.URL = strings.Replace(server.URL, "http://", "http://user:pass@", 1)
|
||||
|
||||
res, err := f.Solve(sb.Context(), c, client.SolveOpt{
|
||||
Exports: exports,
|
||||
FrontendAttrs: map[string]string{
|
||||
dockerui.DefaultLocalNameContext: server.URL + "/.git#buildinfo",
|
||||
dockerui.DefaultLocalNameContext: server.URL + "/.git#buildinfo",
|
||||
dockerui.DefaultLocalNameContext + ":foo": "https://foo:bar@example.invalid/foo.html",
|
||||
},
|
||||
}, nil)
|
||||
require.NoError(t, err)
|
||||
@@ -110,7 +115,9 @@ COPY --from=alpine /bin/busybox /alpine-busybox
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Contains(t, bi.Attrs, "context")
|
||||
require.Equal(t, server.URL+"/.git#buildinfo", *bi.Attrs["context"])
|
||||
require.Equal(t, expectedURL+"/.git#buildinfo", *bi.Attrs["context"])
|
||||
|
||||
require.Equal(t, "https://xxxxx:xxxxx@example.invalid/foo.html", *bi.Attrs["context:foo"])
|
||||
|
||||
_, isGateway := f.(*gatewayFrontend)
|
||||
|
||||
@@ -131,11 +138,11 @@ COPY --from=alpine /bin/busybox /alpine-busybox
|
||||
assert.NotEmpty(t, sources[1].Pin)
|
||||
|
||||
assert.Equal(t, binfotypes.SourceTypeGit, sources[2].Type)
|
||||
assert.Equal(t, server.URL+"/.git#buildinfo", sources[2].Ref)
|
||||
assert.Equal(t, expectedURL+"/.git#buildinfo", sources[2].Ref)
|
||||
assert.NotEmpty(t, sources[2].Pin)
|
||||
|
||||
assert.Equal(t, binfotypes.SourceTypeHTTP, sources[3].Type)
|
||||
assert.Equal(t, "https://raw.githubusercontent.com/moby/moby/v20.10.21/README.md", sources[3].Ref)
|
||||
assert.Equal(t, "https://xxxxx:xxxxx@raw.githubusercontent.com/moby/moby/v20.10.21/README.md", sources[3].Ref)
|
||||
assert.Equal(t, "sha256:419455202b0ef97e480d7f8199b26a721a417818bc0e2d106975f74323f25e6c", sources[3].Pin)
|
||||
}
|
||||
|
||||
|
||||
@@ -77,9 +77,10 @@ RUN echo "ok" > /foo
|
||||
"attest:provenance": provReq,
|
||||
"build-arg:FOO": "bar",
|
||||
"label:lbl": "abc",
|
||||
"vcs:source": "https://example.invalid/repo.git",
|
||||
"vcs:source": "https://user:pass@example.invalid/repo.git",
|
||||
"vcs:revision": "123456",
|
||||
"filename": "Dockerfile",
|
||||
dockerui.DefaultLocalNameContext + ":foo": "https://foo:bar@example.invalid/foo.html",
|
||||
},
|
||||
Exports: []client.ExportEntry{
|
||||
{
|
||||
@@ -137,7 +138,7 @@ RUN echo "ok" > /foo
|
||||
require.Equal(t, "gateway.v0", pred.Invocation.Parameters.Frontend)
|
||||
|
||||
if mode == "max" || mode == "" {
|
||||
require.Equal(t, 3, len(args), "%v", args)
|
||||
require.Equal(t, 4, len(args), "%v", args)
|
||||
require.True(t, pred.Metadata.Completeness.Parameters)
|
||||
|
||||
require.Equal(t, "bar", args["build-arg:FOO"])
|
||||
@@ -145,22 +146,24 @@ RUN echo "ok" > /foo
|
||||
require.Contains(t, args["source"], "buildkit_test/")
|
||||
} else {
|
||||
require.False(t, pred.Metadata.Completeness.Parameters)
|
||||
require.Equal(t, 1, len(args), "%v", args)
|
||||
require.Equal(t, 2, len(args), "%v", args)
|
||||
require.Contains(t, args["source"], "buildkit_test/")
|
||||
}
|
||||
require.Equal(t, "https://xxxxx:xxxxx@example.invalid/foo.html", args["context:foo"])
|
||||
} else {
|
||||
require.Equal(t, "dockerfile.v0", pred.Invocation.Parameters.Frontend)
|
||||
|
||||
if mode == "max" || mode == "" {
|
||||
require.Equal(t, 2, len(args))
|
||||
require.Equal(t, 3, len(args))
|
||||
require.True(t, pred.Metadata.Completeness.Parameters)
|
||||
|
||||
require.Equal(t, "bar", args["build-arg:FOO"])
|
||||
require.Equal(t, "abc", args["label:lbl"])
|
||||
} else {
|
||||
require.False(t, pred.Metadata.Completeness.Parameters)
|
||||
require.Equal(t, 0, len(args), "%v", args)
|
||||
require.Equal(t, 1, len(args), "%v", args)
|
||||
}
|
||||
require.Equal(t, "https://xxxxx:xxxxx@example.invalid/foo.html", args["context:foo"])
|
||||
}
|
||||
|
||||
expectedBase := "pkg:docker/busybox@latest?platform=" + url.PathEscape(platforms.Format(platforms.Normalize(platforms.DefaultSpec())))
|
||||
@@ -177,7 +180,7 @@ RUN echo "ok" > /foo
|
||||
|
||||
if !isClient {
|
||||
require.Equal(t, "Dockerfile", pred.Invocation.ConfigSource.EntryPoint)
|
||||
require.Equal(t, "https://example.invalid/repo.git", pred.Metadata.BuildKitMetadata.VCS["source"])
|
||||
require.Equal(t, "https://xxxxx:xxxxx@example.invalid/repo.git", pred.Metadata.BuildKitMetadata.VCS["source"])
|
||||
require.Equal(t, "123456", pred.Metadata.BuildKitMetadata.VCS["revision"])
|
||||
}
|
||||
|
||||
@@ -265,6 +268,11 @@ COPY myapp.Dockerfile /
|
||||
|
||||
target := registry + "/buildkit/testwithprovenance:git"
|
||||
|
||||
// inject dummy credentials to test that they are masked
|
||||
expectedURL := strings.Replace(server.URL, "http://", "http://xxxxx:xxxxx@", 1)
|
||||
require.NotEqual(t, expectedURL, server.URL)
|
||||
server.URL = strings.Replace(server.URL, "http://", "http://user:pass@", 1)
|
||||
|
||||
_, err = f.Solve(sb.Context(), c, client.SolveOpt{
|
||||
FrontendAttrs: map[string]string{
|
||||
"context": server.URL + "/.git#v1",
|
||||
@@ -318,7 +326,7 @@ COPY myapp.Dockerfile /
|
||||
require.Equal(t, "", pred.Invocation.ConfigSource.EntryPoint)
|
||||
} else {
|
||||
require.NotEmpty(t, pred.Invocation.Parameters.Frontend)
|
||||
require.Equal(t, server.URL+"/.git#v1", pred.Invocation.ConfigSource.URI)
|
||||
require.Equal(t, expectedURL+"/.git#v1", pred.Invocation.ConfigSource.URI)
|
||||
require.Equal(t, "myapp.Dockerfile", pred.Invocation.ConfigSource.EntryPoint)
|
||||
}
|
||||
|
||||
@@ -332,7 +340,7 @@ COPY myapp.Dockerfile /
|
||||
require.Equal(t, expBase, pred.Materials[1].URI)
|
||||
require.NotEmpty(t, pred.Materials[1].Digest["sha256"])
|
||||
|
||||
require.Equal(t, server.URL+"/.git#v1", pred.Materials[2].URI)
|
||||
require.Equal(t, expectedURL+"/.git#v1", pred.Materials[2].URI)
|
||||
require.Equal(t, strings.TrimSpace(string(expectedGitSHA)), pred.Materials[2].Digest["sha1"])
|
||||
} else {
|
||||
require.Equal(t, 2, len(pred.Materials), "%+v", pred.Materials)
|
||||
@@ -340,7 +348,7 @@ COPY myapp.Dockerfile /
|
||||
require.Equal(t, expBase, pred.Materials[0].URI)
|
||||
require.NotEmpty(t, pred.Materials[0].Digest["sha256"])
|
||||
|
||||
require.Equal(t, server.URL+"/.git#v1", pred.Materials[1].URI)
|
||||
require.Equal(t, expectedURL+"/.git#v1", pred.Materials[1].URI)
|
||||
require.Equal(t, strings.TrimSpace(string(expectedGitSHA)), pred.Materials[1].Digest["sha1"])
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
distreference "github.com/docker/distribution/reference"
|
||||
"github.com/moby/buildkit/solver/result"
|
||||
"github.com/moby/buildkit/util/urlutil"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
@@ -190,6 +191,7 @@ func (c *Capture) AddLocal(l LocalSource) {
|
||||
}
|
||||
|
||||
func (c *Capture) AddGit(g GitSource) {
|
||||
g.URL = urlutil.RedactCredentials(g.URL)
|
||||
for _, v := range c.Sources.Git {
|
||||
if v.URL == g.URL {
|
||||
return
|
||||
@@ -199,6 +201,7 @@ func (c *Capture) AddGit(g GitSource) {
|
||||
}
|
||||
|
||||
func (c *Capture) AddHTTP(h HTTPSource) {
|
||||
h.URL = urlutil.RedactCredentials(h.URL)
|
||||
for _, v := range c.Sources.HTTP {
|
||||
if v.URL == h.URL {
|
||||
return
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
slsa "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/common"
|
||||
slsa02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
|
||||
"github.com/moby/buildkit/util/purl"
|
||||
"github.com/moby/buildkit/util/urlutil"
|
||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/package-url/packageurl-go"
|
||||
)
|
||||
@@ -151,6 +152,7 @@ func NewPredicate(c *Capture) (*ProvenancePredicate, error) {
|
||||
} else {
|
||||
inv.ConfigSource.URI = v
|
||||
}
|
||||
inv.ConfigSource.URI = urlutil.RedactCredentials(inv.ConfigSource.URI)
|
||||
delete(c.Args, contextKey)
|
||||
}
|
||||
|
||||
@@ -162,6 +164,9 @@ func NewPredicate(c *Capture) (*ProvenancePredicate, error) {
|
||||
vcs := make(map[string]string)
|
||||
for k, v := range c.Args {
|
||||
if strings.HasPrefix(k, "vcs:") {
|
||||
if k == "vcs:source" {
|
||||
v = urlutil.RedactCredentials(v)
|
||||
}
|
||||
delete(c.Args, k)
|
||||
if v != "" {
|
||||
vcs[strings.TrimPrefix(k, "vcs:")] = v
|
||||
@@ -231,6 +236,11 @@ func FilterArgs(m map[string]string) map[string]string {
|
||||
"platform": {},
|
||||
"cache-imports": {},
|
||||
}
|
||||
const defaultContextKey = "context"
|
||||
contextKey := defaultContextKey
|
||||
if v, ok := m["contextkey"]; ok && v != "" {
|
||||
contextKey = v
|
||||
}
|
||||
out := make(map[string]string)
|
||||
for k, v := range m {
|
||||
if _, ok := hostSpecificArgs[k]; ok {
|
||||
@@ -239,6 +249,9 @@ func FilterArgs(m map[string]string) map[string]string {
|
||||
if strings.HasPrefix(k, "attest:") {
|
||||
continue
|
||||
}
|
||||
if k == contextKey || strings.HasPrefix(k, defaultContextKey+":") {
|
||||
v = urlutil.RedactCredentials(v)
|
||||
}
|
||||
out[k] = v
|
||||
}
|
||||
return out
|
||||
|
||||
Reference in New Issue
Block a user