diff --git a/internal/builder/trusted_builder.go b/internal/builder/trusted_builder.go index 39984c5f8..1cbfd17b5 100644 --- a/internal/builder/trusted_builder.go +++ b/internal/builder/trusted_builder.go @@ -109,12 +109,11 @@ var KnownBuilders = []KnownBuilder{ } func IsKnownTrustedBuilder(builderName string) bool { - for _, knownBuilder := range KnownBuilders { - if builderName == knownBuilder.Image && knownBuilder.Trusted { - return true - } + builderReference, err := name.ParseReference(builderName, name.WithDefaultTag("")) + if err != nil { + return false } - return false + return matchesAnyKnownTrustedBuilder(builderReference) } func IsTrustedBuilder(cfg config.Config, builderName string) (bool, error) { @@ -122,20 +121,45 @@ func IsTrustedBuilder(cfg config.Config, builderName string) (bool, error) { if err != nil { return false, err } + + if matchesAnyKnownTrustedBuilder(builderReference) { + return true, nil + } + for _, trustedBuilder := range cfg.TrustedBuilders { trustedBuilderReference, err := name.ParseReference(trustedBuilder.Name, name.WithDefaultTag("")) if err != nil { return false, err } - if trustedBuilderReference.Identifier() != "" { - if builderReference.Name() == trustedBuilderReference.Name() { - return true, nil - } - } else { - if builderReference.Context().RepositoryStr() == trustedBuilderReference.Context().RepositoryStr() { - return true, nil - } + if referencesMatch(builderReference, trustedBuilderReference) { + return true, nil } } + return false, nil } + +func matchesAnyKnownTrustedBuilder(builderReference name.Reference) bool { + for _, knownBuilder := range KnownBuilders { + if !knownBuilder.Trusted { + continue + } + trustedBuilderReference, err := name.ParseReference(knownBuilder.Image, name.WithDefaultTag("")) + if err != nil { + continue + } + if referencesMatch(builderReference, trustedBuilderReference) { + return true + } + } + return false +} + +// referencesMatch reports whether builderReference is trusted by trustedBuilderReference. +// Tagless trusted entries match any tag in the same repository; tagged entries require an exact match. +func referencesMatch(builderReference, trustedBuilderReference name.Reference) bool { + if trustedBuilderReference.Identifier() != "" { + return builderReference.Name() == trustedBuilderReference.Name() + } + return builderReference.Context().RepositoryStr() == trustedBuilderReference.Context().RepositoryStr() +} diff --git a/internal/builder/trusted_builder_test.go b/internal/builder/trusted_builder_test.go index 90e7599cd..16fb047f7 100644 --- a/internal/builder/trusted_builder_test.go +++ b/internal/builder/trusted_builder_test.go @@ -21,15 +21,39 @@ func TestTrustedBuilder(t *testing.T) { func trustedBuilder(t *testing.T, when spec.G, it spec.S) { when("IsKnownTrustedBuilder", func() { - it("matches exactly", func() { + it("matches tagless known builders against any tag in the same repository", func() { h.AssertTrue(t, bldr.IsKnownTrustedBuilder("paketobuildpacks/builder-jammy-base")) - h.AssertFalse(t, bldr.IsKnownTrustedBuilder("paketobuildpacks/builder-jammy-base:latest")) - h.AssertFalse(t, bldr.IsKnownTrustedBuilder("paketobuildpacks/builder-jammy-base:1.2.3")) + h.AssertTrue(t, bldr.IsKnownTrustedBuilder("paketobuildpacks/builder-jammy-base:latest")) + h.AssertTrue(t, bldr.IsKnownTrustedBuilder("paketobuildpacks/builder-jammy-base:1.2.3")) + }) + it("requires an exact tag match for tagged known builders", func() { + h.AssertTrue(t, bldr.IsKnownTrustedBuilder("heroku/builder:24")) + h.AssertFalse(t, bldr.IsKnownTrustedBuilder("heroku/builder")) + h.AssertFalse(t, bldr.IsKnownTrustedBuilder("heroku/builder:99")) + }) + it("does not match unknown builders", func() { h.AssertFalse(t, bldr.IsKnownTrustedBuilder("my/private/builder")) }) }) when("IsTrustedBuilder", func() { + it("trusts known trusted builders", func() { + // Known builder with exact tag match + isTrusted, err := bldr.IsTrustedBuilder(config.Config{}, "heroku/builder:24") + h.AssertNil(t, err) + h.AssertTrue(t, isTrusted) + + // Known builder without tag should match any tag + isTrusted, err = bldr.IsTrustedBuilder(config.Config{}, "paketobuildpacks/builder-jammy-base:latest") + h.AssertNil(t, err) + h.AssertTrue(t, isTrusted) + + // Unknown builder should not be trusted + isTrusted, err = bldr.IsTrustedBuilder(config.Config{}, "my/private/builder") + h.AssertNil(t, err) + h.AssertFalse(t, isTrusted) + }) + it("trust image without tag", func() { cfg := config.Config{ TrustedBuilders: []config.TrustedBuilder{ diff --git a/internal/commands/build.go b/internal/commands/build.go index d3db7a69b..2b75bcbb5 100644 --- a/internal/commands/build.go +++ b/internal/commands/build.go @@ -121,7 +121,7 @@ func Build(logger logging.Logger, cfg config.Config, packClient PackClient) *cob if err != nil { return err } - trustBuilder := isTrusted || bldr.IsKnownTrustedBuilder(builder) || flags.TrustBuilder + trustBuilder := isTrusted || flags.TrustBuilder if trustBuilder { logger.Debugf("Builder %s is trusted", style.Symbol(builder)) if flags.LifecycleImage != "" { diff --git a/internal/commands/build_test.go b/internal/commands/build_test.go index 254a9d2d2..e4087d5c1 100644 --- a/internal/commands/build_test.go +++ b/internal/commands/build_test.go @@ -139,6 +139,19 @@ func testBuildCommand(t *testing.T, when spec.G, it spec.S) { }) }) + when("the builder is a tagged reference of a tagless known trusted builder", func() { + it("sets the trust builder option", func() { + mockClient.EXPECT(). + Build(gomock.Any(), EqBuildOptionsWithTrustedBuilder(true)). + Return(nil) + + logger.WantVerbose(true) + command.SetArgs([]string{"image", "--builder", "paketobuildpacks/builder-jammy-base:latest"}) + h.AssertNil(t, command.Execute()) + h.AssertContains(t, outBuf.String(), "Builder 'paketobuildpacks/builder-jammy-base:latest' is trusted") + }) + }) + when("the image name matches a builder name", func() { it("refuses to build", func() { logger.WantVerbose(true) diff --git a/internal/commands/builder_inspect.go b/internal/commands/builder_inspect.go index 04d014c45..36fa93be9 100644 --- a/internal/commands/builder_inspect.go +++ b/internal/commands/builder_inspect.go @@ -71,7 +71,7 @@ func inspectBuilder( builderInfo := writer.SharedBuilderInfo{ Name: imageName, IsDefault: imageName == cfg.DefaultBuilder, - Trusted: isTrusted || bldr.IsKnownTrustedBuilder(imageName), + Trusted: isTrusted, } localInfo, localErr := inspector.InspectBuilder(imageName, true, client.WithDetectionOrderDepth(flags.Depth)) diff --git a/internal/commands/builder_inspect_test.go b/internal/commands/builder_inspect_test.go index 60dd5f762..2eafab551 100644 --- a/internal/commands/builder_inspect_test.go +++ b/internal/commands/builder_inspect_test.go @@ -234,6 +234,25 @@ func testBuilderInspectCommand(t *testing.T, when spec.G, it spec.S) { }) }) + when("image is a tagged reference of a tagless known trusted builder", func() { + it("passes builder info with trusted true to the writer's `Print` method", func() { + builderWriter := newDefaultBuilderWriter() + + command := commands.BuilderInspect( + logger, + config.Config{}, + newDefaultBuilderInspector(), + newWriterFactory(returnsForWriter(builderWriter)), + ) + command.SetArgs([]string{"paketobuildpacks/builder-jammy-base:latest"}) + + err := command.Execute() + assert.Nil(err) + + assert.Equal(builderWriter.ReceivedBuilderInfo.Trusted, true) + }) + }) + when("default builder is configured and is the same as specified by the command", func() { it("passes builder info with isDefault true to the writer's `Print` method", func() { cfg.DefaultBuilder = "the/default-builder" diff --git a/internal/commands/config_trusted_builder.go b/internal/commands/config_trusted_builder.go index 36bcfb601..713feaa0f 100644 --- a/internal/commands/config_trusted_builder.go +++ b/internal/commands/config_trusted_builder.go @@ -55,7 +55,7 @@ func addTrustedBuilder(args []string, logger logging.Logger, cfg config.Config, if err != nil { return err } - if isTrusted || bldr.IsKnownTrustedBuilder(imageName) { + if isTrusted { logger.Infof("Builder %s is already trusted", style.Symbol(imageName)) return nil } diff --git a/internal/commands/config_trusted_builder_test.go b/internal/commands/config_trusted_builder_test.go index ef6cfd72f..be0aae5b3 100644 --- a/internal/commands/config_trusted_builder_test.go +++ b/internal/commands/config_trusted_builder_test.go @@ -186,6 +186,18 @@ func testTrustedBuilderCommand(t *testing.T, when spec.G, it spec.S) { h.AssertEq(t, string(oldContents), "") }) }) + + when("builder is a tagged reference of a tagless known trusted builder", func() { + it("does nothing", func() { + h.AssertNil(t, os.WriteFile(configPath, []byte(""), os.ModePerm)) + + command.SetArgs(append(args, "paketobuildpacks/builder-jammy-base:latest")) + h.AssertNil(t, command.Execute()) + oldContents, err := os.ReadFile(configPath) + h.AssertNil(t, err) + h.AssertEq(t, string(oldContents), "") + }) + }) }) })