Skip to content
Closed
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
107 changes: 107 additions & 0 deletions bake/bake_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1358,3 +1358,110 @@ func TestJSONNullVars(t *testing.T) {
require.NoError(t, err)
require.Equal(t, map[string]*string{"bar": ptrstr("baz")}, m["default"].Args)
}

func TestAttestDuplicates(t *testing.T) {
fp := File{
Name: "docker-bake.hcl",
Data: []byte(
`target "default" {
attest = ["type=sbom", "type=sbom,generator=custom"]
}`),
}
ctx := context.TODO()
m, _, err := ReadTargets(ctx, []File{fp}, []string{"default"}, nil, nil)
require.NoError(t, err)

_, err = TargetsToBuildOpt(m, &Input{})
require.Error(t, err)
}

func TestAttestOverride(t *testing.T) {
ctx := context.TODO()

// file without attest set
fp := File{
Name: "docker-bake.hcl",
Data: []byte(`target "default" {}`),
}

// no override
m, _, err := ReadTargets(ctx, []File{fp}, []string{"default"}, nil, nil)
require.NoError(t, err)
require.Empty(t, m["default"].Attest)

opts, err := TargetsToBuildOpt(m, &Input{})
require.NoError(t, err)
require.Empty(t, opts["default"].Attests)

// with override
m, _, err = ReadTargets(ctx, []File{fp}, []string{"default"}, []string{"*.attest=type=sbom,generator=custom"}, nil)
require.NoError(t, err)
require.Equal(t, []string{"type=sbom,generator=custom"}, m["default"].Attest)

opts, err = TargetsToBuildOpt(m, &Input{})
require.NoError(t, err)
require.Equal(t, map[string]*string{"attest:sbom": ptrstr("type=sbom,generator=custom")}, opts["default"].Attests)

// with disabled=true override
m, _, err = ReadTargets(ctx, []File{fp}, []string{"default"}, []string{"*.attest=type=sbom,disabled=true"}, nil)
require.NoError(t, err)
require.Equal(t, []string{"type=sbom,disabled=true"}, m["default"].Attest)

opts, err = TargetsToBuildOpt(m, &Input{})
require.NoError(t, err)
require.Equal(t, map[string]*string{"attest:sbom": nil}, opts["default"].Attests)

// with disabled=false override
m, _, err = ReadTargets(ctx, []File{fp}, []string{"default"}, []string{"*.attest=type=sbom,disabled=false"}, nil)
require.NoError(t, err)
require.Equal(t, []string{"type=sbom,disabled=false"}, m["default"].Attest)

opts, err = TargetsToBuildOpt(m, &Input{})
require.NoError(t, err)
require.Equal(t, map[string]*string{"attest:sbom": ptrstr("type=sbom,disabled=false")}, opts["default"].Attests)

// file with attest set
fp = File{
Name: "docker-bake.hcl",
Data: []byte(
`target "default" {
attest = ["type=sbom,generator=custom"]
}`),
}

// no override
m, _, err = ReadTargets(ctx, []File{fp}, []string{"default"}, nil, nil)
require.NoError(t, err)
require.Equal(t, []string{"type=sbom,generator=custom"}, m["default"].Attest)

opts, err = TargetsToBuildOpt(m, &Input{})
require.NoError(t, err)
require.Equal(t, map[string]*string{"attest:sbom": ptrstr("type=sbom,generator=custom")}, opts["default"].Attests)

// with duplicate override
m, _, err = ReadTargets(ctx, []File{fp}, []string{"default"}, []string{"*.attest=type=sbom,generator=custom"}, nil)
require.NoError(t, err)
require.Equal(t, []string{"type=sbom,generator=custom"}, m["default"].Attest)

opts, err = TargetsToBuildOpt(m, &Input{})
require.NoError(t, err)
require.Equal(t, map[string]*string{"attest:sbom": ptrstr("type=sbom,generator=custom")}, opts["default"].Attests)

// with disabled=true override
m, _, err = ReadTargets(ctx, []File{fp}, []string{"default"}, []string{"*.attest=type=sbom,disabled=true"}, nil)
require.NoError(t, err)
require.Equal(t, []string{"type=sbom,generator=custom", "type=sbom,disabled=true"}, m["default"].Attest)

opts, err = TargetsToBuildOpt(m, &Input{})
require.NoError(t, err)
require.Equal(t, map[string]*string{"attest:sbom": nil}, opts["default"].Attests)

// with disabled=false override
m, _, err = ReadTargets(ctx, []File{fp}, []string{"default"}, []string{"*.attest=type=sbom,disabled=false"}, nil)
require.NoError(t, err)
require.Equal(t, []string{"type=sbom,generator=custom", "type=sbom,disabled=false"}, m["default"].Attest)

opts, err = TargetsToBuildOpt(m, &Input{})
require.NoError(t, err)
require.Equal(t, map[string]*string{"attest:sbom": ptrstr("type=sbom,generator=custom")}, opts["default"].Attests)
}
40 changes: 24 additions & 16 deletions util/buildflags/attests.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func CanonicalizeAttest(attestType string, in string) string {
return ""
}
if b, err := strconv.ParseBool(in); err == nil {
return fmt.Sprintf("type=%s,enabled=%t", attestType, b)
return fmt.Sprintf("type=%s,disabled=%t", attestType, !b)
}
return fmt.Sprintf("type=%s,%s", attestType, in)
}
Expand All @@ -23,57 +23,65 @@ func ParseAttests(in []string) (map[string]*string, error) {
out := map[string]*string{}
for _, in := range in {
in := in
attestType, enabled, err := parseAttest(in)
attestType, disabled, err := parseAttest(in)
if err != nil {
return nil, err
}

k := "attest:" + attestType
if _, ok := out[k]; ok {
return nil, errors.Errorf("duplicate attestation field %s", attestType)
if disabled == nil {
return nil, errors.Errorf("duplicate attestation field %s", attestType)
}
if *disabled {
out[k] = nil
}
continue
}
if enabled {
out[k] = &in
} else {

if disabled != nil && *disabled {
out[k] = nil
} else {
out[k] = &in
}
}
return out, nil
}

func parseAttest(in string) (string, bool, error) {
func parseAttest(in string) (string, *bool, error) {
if in == "" {
return "", false, nil
return "", nil, nil
}

csvReader := csv.NewReader(strings.NewReader(in))
fields, err := csvReader.Read()
if err != nil {
return "", false, err
return "", nil, err
}

attestType := ""
enabled := true
var disabled *bool
for _, field := range fields {
key, value, ok := strings.Cut(field, "=")
if !ok {
return "", false, errors.Errorf("invalid value %s", field)
return "", nil, errors.Errorf("invalid value %s", field)
}
key = strings.TrimSpace(strings.ToLower(key))

switch key {
case "type":
attestType = value
case "enabled":
enabled, err = strconv.ParseBool(value)
case "disabled":

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.

This change is fine by me, as you said it's internal. PTAL @tonistiigi.

b, err := strconv.ParseBool(value)
if err != nil {
return "", false, err
return "", nil, err
}
disabled = &b
}
}
if attestType == "" {
return "", false, errors.Errorf("attestation type not specified")
return "", nil, errors.Errorf("attestation type not specified")
}

return attestType, enabled, nil
return attestType, disabled, nil
}