diff --git a/commands/bake.go b/commands/bake.go index 391db4c925e6..480949841a58 100644 --- a/commands/bake.go +++ b/commands/bake.go @@ -57,6 +57,7 @@ type bakeOptions struct { sbom string provenance string + policy []string allow []string builder string @@ -120,6 +121,14 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba if in.provenance != "" { overrides = append(overrides, fmt.Sprintf("*.attest=%s", buildflags.CanonicalizeAttest("provenance", in.provenance))) } + + policyOverrides, disablePolicy, err := bakePolicyOverrides(in.policy) + if err != nil { + return err + } else if len(policyOverrides) > 0 { + overrides = append(overrides, policyOverrides...) + } + contextPathHash, _ := os.Getwd() ent, err := bake.ParseEntitlements(in.allow) @@ -241,6 +250,12 @@ func runBake(ctx context.Context, dockerCli command.Cli, targets []string, in ba if err != nil { return err } + if disablePolicy { + policy := buildflags.PolicyConfigs{{Disabled: true}} + for _, t := range tgts { + t.Policy = slices.Clone(policy) + } + } var sourceDateEpoch *string for _, t := range tgts { @@ -526,6 +541,7 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { flags.BoolVar(&options.exportPush, "push", false, `Shorthand for "--set=*.output=type=registry". Conditional.`) flags.StringVar(&options.sbom, "sbom", "", `Shorthand for "--set=*.attest=type=sbom"`) flags.StringVar(&options.provenance, "provenance", "", `Shorthand for "--set=*.attest=type=provenance"`) + flags.StringArrayVar(&options.policy, "policy", []string{}, `Global policy evaluation options (format: "[disabled=true|false][,strict=true|false][,log-level=level]")`) flags.StringArrayVar(&options.overrides, "set", nil, `Override target value (e.g., "targetpattern.key=value")`) flags.StringArrayVar(&options.vars, "var", nil, `Set a variable value (e.g., "name=value")`) flags.StringVar(&options.callFunc, "call", "build", `Set method for evaluating build ("check", "outline", "targets")`) @@ -550,6 +566,30 @@ func bakeCmd(dockerCli command.Cli, rootOpts *rootOptions) *cobra.Command { return cmd } +func bakePolicyOverrides(in []string) ([]string, bool, error) { + configs, err := buildflags.ParsePolicyConfigs(in) + if err != nil { + return nil, false, err + } + overrides := make([]string, 0, len(in)) + for i, cfg := range configs { + if len(cfg.Files) > 0 { + return nil, false, errors.New(`--policy does not accept filename; define policy files in the bake definition`) + } + if cfg.Reset { + return nil, false, errors.New(`--policy does not accept reset; define policy composition in the bake definition`) + } + if cfg.Disabled { + if cfg.Strict != nil || cfg.LogLevel != nil || len(configs) > 1 { + return nil, false, errors.New("disabled policy cannot be combined with other policy flags") + } + return nil, true, nil + } + overrides = append(overrides, "*.policy+="+in[i]) + } + return overrides, false, nil +} + func bakeEnvFiles(lookup func(string string) (string, bool)) ([]string, error) { sep, _ := lookup(bakeEnvFileSeparator) if sep == "" { diff --git a/docs/reference/buildx_bake.md b/docs/reference/buildx_bake.md index 0dd0777fbdb4..939341747a1d 100644 --- a/docs/reference/buildx_bake.md +++ b/docs/reference/buildx_bake.md @@ -25,6 +25,7 @@ Build from a file | [`--load`](#load) | `bool` | | Shorthand for `--set=*.output=type=docker`. Conditional. | | [`--metadata-file`](#metadata-file) | `string` | | Write build result metadata to a file | | [`--no-cache`](#no-cache) | `bool` | | Do not use cache when building the image | +| `--policy` | `stringArray` | | Global policy evaluation options (format: `[disabled=true\|false][,strict=true\|false][,log-level=level]`) | | [`--print`](#print) | `bool` | | Print the options without building | | [`--progress`](#progress) | `string` | `auto` | Set type of progress output (`auto`, `none`, `plain`, `quiet`, `rawjson`, `tty`). Use plain to show container output | | [`--provenance`](#provenance) | `string` | | Shorthand for `--set=*.attest=type=provenance` | diff --git a/tests/policy_bake.go b/tests/policy_bake.go index a417de9bae01..632a1b894091 100644 --- a/tests/policy_bake.go +++ b/tests/policy_bake.go @@ -126,6 +126,7 @@ target "disabled-combined" { cases := []struct { name string target string + policyArgs []string wantErrContains string }{ { @@ -160,15 +161,42 @@ target "disabled-combined" { target: "disabled-combined", wantErrContains: "disabled policy cannot be combined with other policy flags", }, + { + name: "global-disabled-skips-target-policies", + target: "fail-extra", + policyArgs: []string{"--policy", "disabled=true"}, + }, + { + name: "global-policy-rejects-filename", + target: "pass-both", + policyArgs: []string{"--policy", "filename=extra.rego"}, + wantErrContains: "--policy does not accept filename", + }, + { + name: "global-policy-rejects-reset", + target: "pass-both", + policyArgs: []string{"--policy", "reset=true"}, + wantErrContains: "--policy does not accept reset", + }, + { + name: "global-disabled-cannot-combine", + target: "pass-both", + policyArgs: []string{"--policy", "disabled=true,strict=true"}, + wantErrContains: "disabled policy cannot be combined with other policy flags", + }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - cmd := buildxCmd(sb, withDir(dir), withArgs( + args := []string{ "bake", "--progress=plain", "--file", "docker-bake.hcl", - tc.target, + } + args = append(args, tc.policyArgs...) + args = append(args, tc.target) + cmd := buildxCmd(sb, withDir(dir), withArgs( + args..., )) out, err := cmd.CombinedOutput() if tc.wantErrContains == "" {