Merge pull request #3807 from tonistiigi/buildx-default-policy
policy: add builtin default source policy
This commit is contained in:
@@ -65,6 +65,30 @@ var sendGitQueryAsInput = sync.OnceValue(func() bool {
|
||||
return false
|
||||
})
|
||||
|
||||
// defaultPolicyEnabled reports whether the builtin default source policy is
|
||||
// enabled via the BUILDX_DEFAULT_POLICY environment variable. It is opt-in
|
||||
// for now; a future release may flip the default to on.
|
||||
var defaultPolicyEnabled = sync.OnceValue(func() bool {
|
||||
if v, ok := os.LookupEnv("BUILDX_DEFAULT_POLICY"); ok {
|
||||
if vv, err := strconv.ParseBool(v); err == nil {
|
||||
return vv
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
// policyExplicitlyDisabled reports whether the user passed `--policy
|
||||
// disabled=true`, which suppresses both user-defined and builtin default
|
||||
// policies.
|
||||
func policyExplicitlyDisabled(configs []buildflags.PolicyConfig) bool {
|
||||
for _, cfg := range configs {
|
||||
if cfg.Disabled {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type policyProgressLogger struct {
|
||||
ch chan *client.SolveStatus
|
||||
done chan struct{}
|
||||
@@ -624,6 +648,22 @@ func configureSourcePolicy(ctx context.Context, np *noderesolver.ResolvedNode, o
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Prepend the builtin default policy when enabled and not explicitly
|
||||
// disabled. The default policy verifies trust for Docker-managed images
|
||||
// (docker/dockerfile, docker/dockerfile-upstream) that may be implicitly
|
||||
// loaded during a build, and passes through any other source so user
|
||||
// policies retain full control.
|
||||
if defaultPolicyEnabled() && !policyExplicitlyDisabled(opt.Policy) {
|
||||
builtin := policyOpt{
|
||||
Files: []policyFileSpec{{
|
||||
Filename: policy.DefaultPolicyFilename,
|
||||
Data: policy.DefaultPolicyData(),
|
||||
}},
|
||||
}
|
||||
popts = append([]policyOpt{builtin}, popts...)
|
||||
}
|
||||
|
||||
if len(popts) == 0 {
|
||||
so.SourcePolicyProvider = nil
|
||||
return nil, nil
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package policy
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
// DefaultPolicyFilename is the synthetic filename used for the embedded
|
||||
// default policy when it is loaded as a regular policy file.
|
||||
const DefaultPolicyFilename = "buildx_default_policy.rego"
|
||||
|
||||
//go:embed default.rego
|
||||
var defaultPolicyModule []byte
|
||||
|
||||
// DefaultPolicyData returns the embedded default policy module bytes.
|
||||
func DefaultPolicyData() []byte {
|
||||
return defaultPolicyModule
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
package docker
|
||||
|
||||
# Default policy embedded in Buildx. It verifies trust for images shipped
|
||||
# by Docker that may be implicitly loaded during a build:
|
||||
#
|
||||
# - docker/dockerfile
|
||||
# - docker/dockerfile-upstream
|
||||
# - docker/buildkit-syft-scanner
|
||||
#
|
||||
# Any image outside this managed set is allowed and passes through to user
|
||||
# policies unchanged. Access by digest is always allowed. For tag-based
|
||||
# access the rules below enforce a signed release from the expected GitHub
|
||||
# source repository using the existing docker_github_builder_signature
|
||||
# helper from builtins.rego.
|
||||
|
||||
is_dockerfile if {
|
||||
input.image
|
||||
input.image.fullRepo == "docker.io/docker/dockerfile"
|
||||
}
|
||||
|
||||
is_dockerfile if {
|
||||
input.image
|
||||
input.image.fullRepo == "docker.io/docker/dockerfile-upstream"
|
||||
}
|
||||
|
||||
is_syft_scanner if {
|
||||
input.image
|
||||
input.image.fullRepo == "docker.io/docker/buildkit-syft-scanner"
|
||||
}
|
||||
|
||||
dockerfile_floating_tag(tag) if tag == "latest"
|
||||
dockerfile_floating_tag(tag) if tag == "labs"
|
||||
dockerfile_floating_tag(tag) if tag == "master"
|
||||
|
||||
dockerfile_tag_requires_sig(tag) if dockerfile_floating_tag(tag)
|
||||
dockerfile_tag_requires_sig(tag) if version_tag_ge(tag, 1, 21)
|
||||
|
||||
syft_scanner_floating_tag(tag) if tag == "latest"
|
||||
|
||||
syft_scanner_tag_requires_sig(tag) if syft_scanner_floating_tag(tag)
|
||||
syft_scanner_tag_requires_sig(tag) if version_tag_ge(tag, 1, 10)
|
||||
|
||||
|
||||
default_policy_deny_msgs contains msg if {
|
||||
is_dockerfile
|
||||
tag := input.image.tag
|
||||
tag != ""
|
||||
dockerfile_tag_requires_sig(tag)
|
||||
not dockerfile_sig_ok(tag)
|
||||
msg := sprintf("image %s is not allowed by default policy: a verified docker-github-builder signature is required for %s tag", [input.image.ref, input.image.tag])
|
||||
}
|
||||
|
||||
default_policy_deny_msgs contains msg if {
|
||||
is_syft_scanner
|
||||
tag := input.image.tag
|
||||
tag != ""
|
||||
syft_scanner_tag_requires_sig(tag)
|
||||
not syft_scanner_sig_ok(tag)
|
||||
msg := sprintf("image %s is not allowed by default policy: a verified docker-github-builder signature is required for %s tag", [input.image.ref, input.image.tag])
|
||||
}
|
||||
|
||||
dockerfile_sig_ok(tag) if {
|
||||
dockerfile_floating_tag(tag)
|
||||
some sig in input.image.signatures
|
||||
docker_github_builder_signature(sig, "moby/buildkit")
|
||||
}
|
||||
|
||||
dockerfile_sig_ok(tag) if {
|
||||
not dockerfile_floating_tag(tag)
|
||||
some sig in input.image.signatures
|
||||
docker_github_builder_signature(sig, "moby/buildkit")
|
||||
dockerfile_sig_ref_matches(sig, tag)
|
||||
}
|
||||
|
||||
syft_scanner_sig_ok(tag) if {
|
||||
syft_scanner_floating_tag(tag)
|
||||
some sig in input.image.signatures
|
||||
docker_github_builder_signature(sig, "docker/buildkit-syft-scanner")
|
||||
}
|
||||
|
||||
syft_scanner_sig_ok(tag) if {
|
||||
not syft_scanner_floating_tag(tag)
|
||||
some sig in input.image.signatures
|
||||
docker_github_builder_signature(sig, "docker/buildkit-syft-scanner")
|
||||
syft_scanner_sig_ref_matches(sig, tag)
|
||||
}
|
||||
|
||||
|
||||
decision := {
|
||||
"allow": count(default_policy_deny_msgs) == 0,
|
||||
"deny_msg": [msg | some msg in default_policy_deny_msgs],
|
||||
}
|
||||
|
||||
# ---- helpers ----
|
||||
|
||||
# parse_version returns [major, minor] when tag matches a version pattern
|
||||
# like "1", "1.21", "1.21.0", "1.21.0-labs". For a major-only tag such as
|
||||
# "1", the minor component is treated as effectively unbounded so floating
|
||||
# major tags are handled like the newest release in that major line.
|
||||
parse_version(tag) := [maj, min] if {
|
||||
m := regex.find_all_string_submatch_n(`^(\d+)\.(\d+)(?:\.\d+)?(?:-labs)?$`, tag, 1)
|
||||
count(m) == 1
|
||||
maj := to_number(m[0][1])
|
||||
min := to_number(m[0][2])
|
||||
}
|
||||
|
||||
parse_version(tag) := [maj, 999999] if {
|
||||
m := regex.find_all_string_submatch_n(`^(\d+)(?:-labs)?$`, tag, 1)
|
||||
count(m) == 1
|
||||
maj := to_number(m[0][1])
|
||||
}
|
||||
|
||||
version_tag_ge(tag, target_major, _) if {
|
||||
v := parse_version(tag)
|
||||
v[0] > target_major
|
||||
}
|
||||
|
||||
version_tag_ge(tag, target_major, target_minor) if {
|
||||
v := parse_version(tag)
|
||||
v[0] == target_major
|
||||
v[1] >= target_minor
|
||||
}
|
||||
|
||||
dockerfile_sig_ref_matches(sig, tag) if {
|
||||
sig_ref_matches(sig.signer.sourceRepositoryRef, tag, "refs/tags/dockerfile/")
|
||||
}
|
||||
|
||||
syft_scanner_sig_ref_matches(sig, tag) if {
|
||||
ref := trim_prefix(sig.signer.sourceRepositoryRef, "refs/tags/")
|
||||
ref != sig.signer.sourceRepositoryRef
|
||||
version_tag_selector_matches(tag, ref)
|
||||
}
|
||||
|
||||
sig_ref_matches(ref, tag, prefix) if {
|
||||
stripped_ref := trim_prefix(ref, prefix)
|
||||
stripped_ref != ref
|
||||
tag_labs := endswith(tag, "-labs")
|
||||
ref_labs := endswith(stripped_ref, "-labs")
|
||||
tag_labs == ref_labs
|
||||
version_tag_selector_matches(
|
||||
trim_suffix(tag, "-labs"),
|
||||
trim_suffix(stripped_ref, "-labs"),
|
||||
)
|
||||
}
|
||||
|
||||
version_tag_selector_matches(selector, candidate) if {
|
||||
selector == candidate
|
||||
}
|
||||
|
||||
version_tag_selector_matches(selector, candidate) if {
|
||||
m := regex.find_all_string_submatch_n(`^(\d+)\.(\d+)$`, selector, 1)
|
||||
count(m) == 1
|
||||
parse_version(selector) == parse_version(candidate)
|
||||
}
|
||||
|
||||
version_tag_selector_matches(selector, candidate) if {
|
||||
m := regex.find_all_string_submatch_n(`^(\d+)$`, selector, 1)
|
||||
count(m) == 1
|
||||
sel := parse_version(selector)
|
||||
cand := parse_version(candidate)
|
||||
sel[0] == cand[0]
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
package policy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
gwpb "github.com/moby/buildkit/frontend/gateway/pb"
|
||||
"github.com/moby/buildkit/solver/pb"
|
||||
moby_buildkit_v1_sourcepolicy "github.com/moby/buildkit/sourcepolicy/pb"
|
||||
"github.com/moby/buildkit/sourcepolicy/policysession"
|
||||
policyimage "github.com/moby/policy-helpers/image"
|
||||
policytypes "github.com/moby/policy-helpers/types"
|
||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/sigstore/sigstore-go/pkg/fulcio/certificate"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// makeDefaultPolicy returns a Policy instance backed by the embedded
|
||||
// default policy module, optionally wired to a mock signature verifier
|
||||
// returning the supplied SignatureInfo for image attestations.
|
||||
func makeDefaultPolicy(t *testing.T, sigInfo *policytypes.SignatureInfo) *Policy {
|
||||
t.Helper()
|
||||
|
||||
var verifierProvider PolicyVerifierProvider
|
||||
if sigInfo != nil {
|
||||
verifierProvider = func() (PolicyVerifier, error) {
|
||||
return &mockPolicyVerifier{
|
||||
verifyImage: func(_ context.Context, _ policyimage.ReferrersProvider, _ ocispecs.Descriptor, _ *ocispecs.Platform) (*policytypes.SignatureInfo, error) {
|
||||
return sigInfo, nil
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return NewPolicy(Opt{
|
||||
Files: []File{{
|
||||
Filename: DefaultPolicyFilename,
|
||||
Data: DefaultPolicyData(),
|
||||
}},
|
||||
Log: func(level logrus.Level, msg string) {
|
||||
t.Logf("[%s] %s", level, msg)
|
||||
},
|
||||
VerifierProvider: verifierProvider,
|
||||
})
|
||||
}
|
||||
|
||||
// dockerGithubBuilderSig returns a SignatureInfo that satisfies the
|
||||
// docker_github_builder_signature helper for the given source repository
|
||||
// and ref. Pass an empty ref to omit the SourceRepositoryRef field.
|
||||
func dockerGithubBuilderSig(sourceRepo, sourceRef string) *policytypes.SignatureInfo {
|
||||
return &policytypes.SignatureInfo{
|
||||
Kind: policytypes.KindDockerGithubBuilder,
|
||||
SignatureType: policytypes.SignatureBundleV03,
|
||||
Timestamps: []policytypes.TimestampVerificationResult{
|
||||
{Type: "rekor", URI: "https://rekor.sigstore.dev", Timestamp: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)},
|
||||
},
|
||||
Signer: &certificate.Summary{
|
||||
CertificateIssuer: "CN=sigstore-intermediate,O=sigstore.dev",
|
||||
Extensions: certificate.Extensions{
|
||||
Issuer: "https://token.actions.githubusercontent.com",
|
||||
SourceRepositoryURI: "https://github.com/" + sourceRepo,
|
||||
SourceRepositoryRef: sourceRef,
|
||||
RunnerEnvironment: "github-hosted",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// runDefaultPolicyImage evaluates the default policy against the given image
|
||||
// reference. An attestation chain and an empty image config are always
|
||||
// supplied so that the policy can fully resolve metadata and produce a
|
||||
// decision (rather than requesting more data via the next response).
|
||||
func runDefaultPolicyImage(t *testing.T, p *Policy, ref string) *policysession.DecisionResponse {
|
||||
t.Helper()
|
||||
src := &gwpb.ResolveSourceMetaResponse{
|
||||
Source: &pb.SourceOp{Identifier: "docker-image://" + ref},
|
||||
Image: &gwpb.ResolveSourceImageResponse{
|
||||
Digest: "sha256:abababababababababababababababababababababababababababababababab",
|
||||
Config: []byte(`{"created":"2024-01-01T00:00:00Z","config":{}}`),
|
||||
AttestationChain: newTestAttestationChain(t),
|
||||
},
|
||||
}
|
||||
resp, _, err := p.CheckPolicy(context.Background(), &policysession.CheckPolicyRequest{
|
||||
Platform: &pb.Platform{OS: "linux", Architecture: "amd64"},
|
||||
Source: src,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, resp)
|
||||
return resp
|
||||
}
|
||||
|
||||
func TestDefaultPolicyAllowsNonImageSources(t *testing.T) {
|
||||
p := makeDefaultPolicy(t, nil)
|
||||
src := &gwpb.ResolveSourceMetaResponse{
|
||||
Source: &pb.SourceOp{Identifier: "https://example.com/foo.tar.gz"},
|
||||
HTTP: &gwpb.ResolveSourceHTTPResponse{
|
||||
Checksum: "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
},
|
||||
}
|
||||
resp, _, err := p.CheckPolicy(context.Background(), &policysession.CheckPolicyRequest{
|
||||
Source: src,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, moby_buildkit_v1_sourcepolicy.PolicyAction_ALLOW, resp.Action)
|
||||
}
|
||||
|
||||
func TestDefaultPolicyImages(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
sig *policytypes.SignatureInfo
|
||||
ref string
|
||||
allow bool
|
||||
denyMsg string
|
||||
}{
|
||||
{
|
||||
name: "allows_unrelated_images",
|
||||
ref: "alpine:latest",
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
name: "dockerfile_digest_only_always_allowed",
|
||||
ref: "docker/dockerfile@sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
name: "dockerfile_tagged_digest_denied",
|
||||
ref: "docker/dockerfile:1.21.0@sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
|
||||
denyMsg: "signature is required for 1.21.0 tag",
|
||||
},
|
||||
{
|
||||
name: "dockerfile_old_version_allowed_unsigned",
|
||||
ref: "docker/dockerfile:1.20.0",
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
name: "dockerfile_new_version_requires_signature",
|
||||
ref: "docker/dockerfile:1.21.0",
|
||||
denyMsg: "signature is required for 1.21.0 tag",
|
||||
},
|
||||
{
|
||||
name: "dockerfile_new_minor_version_requires_signature",
|
||||
ref: "docker/dockerfile:1.21",
|
||||
denyMsg: "signature is required for 1.21 tag",
|
||||
},
|
||||
{
|
||||
name: "dockerfile_new_major_version_requires_signature",
|
||||
ref: "docker/dockerfile:1",
|
||||
denyMsg: "signature is required for 1 tag",
|
||||
},
|
||||
{
|
||||
name: "dockerfile_new_version_allowed_with_matching_signature",
|
||||
sig: dockerGithubBuilderSig("moby/buildkit", "refs/tags/dockerfile/1.21.0"),
|
||||
ref: "docker/dockerfile:1.21.0",
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
name: "dockerfile_new_minor_version_allowed_with_matching_patch_signature",
|
||||
sig: dockerGithubBuilderSig("moby/buildkit", "refs/tags/dockerfile/1.21.0"),
|
||||
ref: "docker/dockerfile:1.21",
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
name: "dockerfile_new_version_allowed_with_matching_signature_labs",
|
||||
sig: dockerGithubBuilderSig("moby/buildkit", "refs/tags/dockerfile/1.21.0-labs"),
|
||||
ref: "docker/dockerfile:1.21.0-labs",
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
name: "dockerfile_new_version_allowed_with_matching_signature_labs2",
|
||||
sig: dockerGithubBuilderSig("moby/buildkit", "refs/tags/dockerfile/1.21.0-labs"),
|
||||
ref: "docker/dockerfile:1.21-labs",
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
name: "dockerfile_new_version_allowed_with_matching_signature_labs3",
|
||||
sig: dockerGithubBuilderSig("moby/buildkit", "refs/tags/dockerfile/1.21.0-labs"),
|
||||
ref: "docker/dockerfile:1-labs",
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
name: "dockerfile_new_version_allowed_with_matching_signature_labs4",
|
||||
sig: dockerGithubBuilderSig("moby/buildkit", "refs/tags/dockerfile/1.21.0-labs"),
|
||||
ref: "docker/dockerfile:labs",
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
name: "dockerfile_new_version_denied_with_nonlabs_signature_for_labs_tag",
|
||||
sig: dockerGithubBuilderSig("moby/buildkit", "refs/tags/dockerfile/1.21.0"),
|
||||
ref: "docker/dockerfile:1-labs",
|
||||
denyMsg: "signature is required for 1-labs tag",
|
||||
},
|
||||
{
|
||||
name: "dockerfile_new_version_denied_with_mismatched_ref_labs",
|
||||
sig: dockerGithubBuilderSig("moby/buildkit", "refs/tags/dockerfile/1.22.0-labs"),
|
||||
ref: "docker/dockerfile:1.21.0-labs",
|
||||
denyMsg: "signature is required for 1.21.0-labs tag",
|
||||
},
|
||||
{
|
||||
name: "dockerfile_new_version_denied_with_nonlabs_signature_for_exact_labs_tag",
|
||||
sig: dockerGithubBuilderSig("moby/buildkit", "refs/tags/dockerfile/1.21.0"),
|
||||
ref: "docker/dockerfile:1.21.0-labs",
|
||||
denyMsg: "signature is required for 1.21.0-labs tag",
|
||||
},
|
||||
{
|
||||
name: "dockerfile_new_version_denied_with_wrong_signature_repo",
|
||||
sig: dockerGithubBuilderSig("docker/dockerfile", "refs/tags/dockerfile/1.21.0"),
|
||||
ref: "docker/dockerfile:1.21.0",
|
||||
denyMsg: "signature is required for 1.21.0 tag",
|
||||
},
|
||||
{
|
||||
name: "dockerfile_upstream_new_version_denied_with_wrong_signature_repo",
|
||||
sig: dockerGithubBuilderSig("docker/dockerfile-upstream", "refs/tags/dockerfile/1.21.0"),
|
||||
ref: "docker/dockerfile-upstream:1.21.0",
|
||||
denyMsg: "signature is required for 1.21.0 tag",
|
||||
},
|
||||
{
|
||||
name: "dockerfile_upstream_new_version_requires_signature",
|
||||
ref: "docker/dockerfile-upstream:1.21.0",
|
||||
denyMsg: "signature is required for 1.21.0 tag",
|
||||
},
|
||||
{
|
||||
name: "dockerfile_upstream_new_version_allowed_with_matching_signature",
|
||||
sig: dockerGithubBuilderSig("moby/buildkit", "refs/tags/dockerfile/1.21.0"),
|
||||
ref: "docker/dockerfile-upstream:1.21.0",
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
name: "dockerfile_upstream_new_version_denied_with_mismatched_ref",
|
||||
sig: dockerGithubBuilderSig("moby/buildkit", "refs/tags/dockerfile/1.22.0"),
|
||||
ref: "docker/dockerfile-upstream:1.21.0",
|
||||
denyMsg: "signature is required for 1.21.0 tag",
|
||||
},
|
||||
{
|
||||
name: "dockerfile_new_version_denied_with_mismatched_ref",
|
||||
sig: dockerGithubBuilderSig("moby/buildkit", "refs/tags/dockerfile/1.22.0"),
|
||||
ref: "docker/dockerfile:1.21.0",
|
||||
denyMsg: "signature is required for 1.21.0 tag",
|
||||
},
|
||||
{
|
||||
name: "dockerfile_new_minor_version_denied_with_newer_patch_ref",
|
||||
sig: dockerGithubBuilderSig("moby/buildkit", "refs/tags/dockerfile/1.23.0"),
|
||||
ref: "docker/dockerfile:1.22",
|
||||
denyMsg: "signature is required for 1.22 tag",
|
||||
},
|
||||
{
|
||||
name: "dockerfile_new_version_denied_with_mismatched_ref_labs",
|
||||
sig: dockerGithubBuilderSig("moby/buildkit", "refs/tags/dockerfile/1.22.0-labs"),
|
||||
ref: "docker/dockerfile:1.21.0-labs",
|
||||
denyMsg: "signature is required for 1.21.0-labs tag",
|
||||
},
|
||||
{
|
||||
name: "dockerfile_new_version_denied_with_wrong_signature_tag",
|
||||
sig: dockerGithubBuilderSig("moby/buildkit", "refs/tags/1.21.0"),
|
||||
ref: "docker/dockerfile:1.21.0",
|
||||
denyMsg: "signature is required for 1.21.0 tag",
|
||||
},
|
||||
{
|
||||
name: "dockerfile_latest_allowed_with_signature_any_ref",
|
||||
sig: dockerGithubBuilderSig("moby/buildkit", "refs/tags/dockerfile/1.30.0"),
|
||||
ref: "docker/dockerfile:latest",
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
name: "dockerfile_latest_denied_without_signature",
|
||||
ref: "docker/dockerfile:latest",
|
||||
denyMsg: "signature is required for latest tag",
|
||||
},
|
||||
{
|
||||
name: "dockerfile_upstream_latest_allowed_with_signature_any_ref",
|
||||
sig: dockerGithubBuilderSig("moby/buildkit", "refs/tags/dockerfile/1.30.0"),
|
||||
ref: "docker/dockerfile-upstream:latest",
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
name: "dockerfile_upstream_latest_denied_without_signature",
|
||||
ref: "docker/dockerfile-upstream:latest",
|
||||
denyMsg: "signature is required for latest tag",
|
||||
},
|
||||
{
|
||||
name: "dockerfile_upstream_master_allowed_with_signature_any_ref",
|
||||
sig: dockerGithubBuilderSig("moby/buildkit", "refs/heads/master"),
|
||||
ref: "docker/dockerfile-upstream:master",
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
name: "dockerfile_upstream_master_denied_without_signature",
|
||||
ref: "docker/dockerfile-upstream:master",
|
||||
denyMsg: "signature is required for master tag",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
p := makeDefaultPolicy(t, tc.sig)
|
||||
resp := runDefaultPolicyImage(t, p, tc.ref)
|
||||
if tc.allow {
|
||||
require.Equal(t, moby_buildkit_v1_sourcepolicy.PolicyAction_ALLOW, resp.Action)
|
||||
require.Empty(t, resp.DenyMessages)
|
||||
return
|
||||
}
|
||||
|
||||
require.Equal(t, moby_buildkit_v1_sourcepolicy.PolicyAction_DENY, resp.Action)
|
||||
require.Len(t, resp.DenyMessages, 1)
|
||||
require.Contains(t, resp.DenyMessages[0].Message, tc.denyMsg)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultPolicySyftScannerImages(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
sig *policytypes.SignatureInfo
|
||||
ref string
|
||||
allow bool
|
||||
denyMsg string
|
||||
}{
|
||||
{
|
||||
name: "syft_scanner_old_version_allowed_unsigned",
|
||||
ref: "docker/buildkit-syft-scanner:1.9.0",
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
name: "syft_scanner_new_version_requires_signature",
|
||||
ref: "docker/buildkit-syft-scanner:1.10.0",
|
||||
denyMsg: "signature is required for 1.10.0 tag",
|
||||
},
|
||||
{
|
||||
name: "syft_scanner_new_minor_version_requires_signature",
|
||||
ref: "docker/buildkit-syft-scanner:1.10",
|
||||
denyMsg: "signature is required for 1.10 tag",
|
||||
},
|
||||
{
|
||||
name: "syft_scanner_new_major_version_requires_signature",
|
||||
ref: "docker/buildkit-syft-scanner:1",
|
||||
denyMsg: "signature is required for 1 tag",
|
||||
},
|
||||
{
|
||||
name: "syft_scanner_new_version_allowed_with_matching_signature",
|
||||
sig: dockerGithubBuilderSig("docker/buildkit-syft-scanner", "refs/tags/1.10.0"),
|
||||
ref: "docker/buildkit-syft-scanner:1.10.0",
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
name: "syft_scanner_new_minor_version_allowed_with_matching_patch_signature",
|
||||
sig: dockerGithubBuilderSig("docker/buildkit-syft-scanner", "refs/tags/1.10.0"),
|
||||
ref: "docker/buildkit-syft-scanner:1.10",
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
name: "syft_scanner_new_major_version_allowed_with_matching_minor_signature",
|
||||
sig: dockerGithubBuilderSig("docker/buildkit-syft-scanner", "refs/tags/1.10.0"),
|
||||
ref: "docker/buildkit-syft-scanner:1",
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
name: "syft_scanner_new_version_denied_with_wrong_signature_repo",
|
||||
sig: dockerGithubBuilderSig("moby/buildkit", "refs/tags/1.10.0"),
|
||||
ref: "docker/buildkit-syft-scanner:1.10.0",
|
||||
denyMsg: "signature is required for 1.10.0 tag",
|
||||
},
|
||||
{
|
||||
name: "syft_scanner_new_version_denied_with_mismatched_ref",
|
||||
sig: dockerGithubBuilderSig("docker/buildkit-syft-scanner", "refs/tags/1.11.0"),
|
||||
ref: "docker/buildkit-syft-scanner:1.10.0",
|
||||
denyMsg: "signature is required for 1.10.0 tag",
|
||||
},
|
||||
{
|
||||
name: "syft_scanner_new_minor_version_denied_with_newer_patch_ref",
|
||||
sig: dockerGithubBuilderSig("docker/buildkit-syft-scanner", "refs/tags/1.11.0"),
|
||||
ref: "docker/buildkit-syft-scanner:1.10",
|
||||
denyMsg: "signature is required for 1.10 tag",
|
||||
},
|
||||
{
|
||||
name: "syft_scanner_latest_allowed_with_signature_any_ref",
|
||||
sig: dockerGithubBuilderSig("docker/buildkit-syft-scanner", "refs/tags/1.10.0"),
|
||||
ref: "docker/buildkit-syft-scanner:latest",
|
||||
allow: true,
|
||||
},
|
||||
{
|
||||
name: "syft_scanner_latest_denied_without_signature",
|
||||
ref: "docker/buildkit-syft-scanner:latest",
|
||||
denyMsg: "signature is required for latest tag",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
p := makeDefaultPolicy(t, tc.sig)
|
||||
resp := runDefaultPolicyImage(t, p, tc.ref)
|
||||
if tc.allow {
|
||||
require.Equal(t, moby_buildkit_v1_sourcepolicy.PolicyAction_ALLOW, resp.Action)
|
||||
require.Empty(t, resp.DenyMessages)
|
||||
return
|
||||
}
|
||||
|
||||
require.Equal(t, moby_buildkit_v1_sourcepolicy.PolicyAction_DENY, resp.Action)
|
||||
require.Len(t, resp.DenyMessages, 1)
|
||||
require.Contains(t, resp.DenyMessages[0].Message, tc.denyMsg)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user