Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 37 additions & 13 deletions internal/builder/trusted_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,33 +109,57 @@ 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) {
builderReference, err := name.ParseReference(builderName, name.WithDefaultTag(""))
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()
}
30 changes: 27 additions & 3 deletions internal/builder/trusted_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this test case reference: paketobuildpacks/builder-jammy-base?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The :latest here is intentional - this case exercises the "known entry has no tag, match any tag in the same repository" branch of the new name.ParseReference logic, which is what #2572 is about and is also the example shown in the Before/After in the PR description.

The heroku/builder:24 case above covers the exact-tag branch. Switching to a bare paketobuildpacks/builder-jammy-base would collapse this into a bare-vs-bare repository match and stop exercising the "any tag" path.

Happy to expand the inline comment if it'd read more clearly, or add a second assertion with a different tag (e.g. :anything) to make the "any tag" intent more obvious - let me know what you'd prefer (or if I'm missing something obvious :)).

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{
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 != "" {
Expand Down
13 changes: 13 additions & 0 deletions internal/commands/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/builder_inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
19 changes: 19 additions & 0 deletions internal/commands/builder_inspect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/config_trusted_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
12 changes: 12 additions & 0 deletions internal/commands/config_trusted_builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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), "")
})
})
})
})

Expand Down
Loading