Merge pull request #3611 from tonistiigi/policy-update-v0.31-rc2
Updates and fixes to policy support
This commit is contained in:
+102
-16
@@ -48,6 +48,7 @@ import (
|
||||
"github.com/moby/buildkit/util/entitlements"
|
||||
"github.com/moby/buildkit/util/gitutil"
|
||||
"github.com/opencontainers/go-digest"
|
||||
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/tonistiigi/fsutil"
|
||||
@@ -66,29 +67,28 @@ type policyProgressLogger struct {
|
||||
ch chan *client.SolveStatus
|
||||
done chan struct{}
|
||||
dgst digest.Digest
|
||||
started time.Time
|
||||
name string
|
||||
started time.Time
|
||||
mu sync.Mutex
|
||||
timer *time.Timer
|
||||
window int
|
||||
open bool
|
||||
closed bool
|
||||
}
|
||||
|
||||
const policyProgressWindow = 500 * time.Millisecond
|
||||
|
||||
func newPolicyProgressLogger(pw progress.Writer, name string) *policyProgressLogger {
|
||||
if pw == nil {
|
||||
return nil
|
||||
}
|
||||
ch, done := progress.NewChannel(pw)
|
||||
dgst := digest.FromBytes([]byte(identity.NewID()))
|
||||
tm := time.Now()
|
||||
vtx := client.Vertex{
|
||||
Digest: dgst,
|
||||
Name: name,
|
||||
Started: &tm,
|
||||
}
|
||||
ch <- &client.SolveStatus{Vertexes: []*client.Vertex{&vtx}}
|
||||
return &policyProgressLogger{
|
||||
ch: ch,
|
||||
done: done,
|
||||
dgst: dgst,
|
||||
started: tm,
|
||||
name: name,
|
||||
ch: ch,
|
||||
done: done,
|
||||
dgst: dgst,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +96,34 @@ func (l *policyProgressLogger) Log(msg string) {
|
||||
if l == nil || msg == "" {
|
||||
return
|
||||
}
|
||||
needStart := false
|
||||
var started time.Time
|
||||
var window int
|
||||
|
||||
l.mu.Lock()
|
||||
if l.closed {
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
if !l.open {
|
||||
needStart = true
|
||||
l.open = true
|
||||
l.window++
|
||||
window = l.window
|
||||
started = time.Now()
|
||||
l.started = started
|
||||
} else {
|
||||
window = l.window
|
||||
}
|
||||
if l.timer != nil {
|
||||
l.timer.Stop()
|
||||
}
|
||||
l.timer = time.AfterFunc(policyProgressWindow, func() {
|
||||
l.completeWindow(window, nil)
|
||||
})
|
||||
if needStart {
|
||||
l.sendVertexStart(started)
|
||||
}
|
||||
if !strings.HasSuffix(msg, "\n") {
|
||||
msg += "\n"
|
||||
}
|
||||
@@ -107,6 +135,7 @@ func (l *policyProgressLogger) Log(msg string) {
|
||||
Timestamp: time.Now(),
|
||||
}},
|
||||
}
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
func (l *policyProgressLogger) Write(p []byte) (int, error) {
|
||||
@@ -120,19 +149,66 @@ func (l *policyProgressLogger) Close(err error) {
|
||||
if l == nil {
|
||||
return
|
||||
}
|
||||
shouldComplete := false
|
||||
var started time.Time
|
||||
|
||||
l.mu.Lock()
|
||||
if l.closed {
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
l.closed = true
|
||||
if l.open {
|
||||
shouldComplete = true
|
||||
started = l.started
|
||||
l.open = false
|
||||
}
|
||||
l.window++
|
||||
if l.timer != nil {
|
||||
l.timer.Stop()
|
||||
l.timer = nil
|
||||
}
|
||||
if shouldComplete {
|
||||
l.sendVertexComplete(started, err)
|
||||
}
|
||||
l.mu.Unlock()
|
||||
close(l.ch)
|
||||
<-l.done
|
||||
}
|
||||
|
||||
func (l *policyProgressLogger) completeWindow(window int, err error) {
|
||||
l.mu.Lock()
|
||||
if l.closed || !l.open || window != l.window {
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
started := l.started
|
||||
l.open = false
|
||||
l.sendVertexComplete(started, err)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
func (l *policyProgressLogger) sendVertexStart(started time.Time) {
|
||||
vtx := client.Vertex{
|
||||
Digest: l.dgst,
|
||||
Name: l.name,
|
||||
Started: &started,
|
||||
}
|
||||
l.ch <- &client.SolveStatus{Vertexes: []*client.Vertex{&vtx}}
|
||||
}
|
||||
|
||||
func (l *policyProgressLogger) sendVertexComplete(started time.Time, err error) {
|
||||
tm := time.Now()
|
||||
vtx := client.Vertex{
|
||||
Digest: l.dgst,
|
||||
Name: l.name,
|
||||
Started: &l.started,
|
||||
Started: &started,
|
||||
Completed: &tm,
|
||||
}
|
||||
if err != nil {
|
||||
vtx.Error = err.Error()
|
||||
}
|
||||
l.ch <- &client.SolveStatus{Vertexes: []*client.Vertex{&vtx}}
|
||||
close(l.ch)
|
||||
<-l.done
|
||||
}
|
||||
|
||||
func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt *Options, bopts gateway.BuildOpts, cfg *confutil.Config, pw progress.Writer, docker *dockerutil.Client) (_ *client.SolveOpt, release func(), err error) {
|
||||
@@ -457,6 +533,7 @@ func toSolveOpt(ctx context.Context, node builder.Node, multiDriver bool, opt *O
|
||||
Log: logf,
|
||||
FS: opt.Inputs.policy.FS,
|
||||
VerifierProvider: policy.SignatureVerifier(cfg),
|
||||
DefaultPlatform: defaultPlatform(bopts),
|
||||
})
|
||||
cbs = append(cbs, p.CheckPolicy)
|
||||
if popt.Strict {
|
||||
@@ -1233,3 +1310,12 @@ func parseOCILayoutPath(s string) (localPath, dgst, tag string) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func defaultPlatform(bopts gateway.BuildOpts) *ocispecs.Platform {
|
||||
pl := bopts.Workers[0].Platforms
|
||||
if len(pl) == 0 {
|
||||
return nil
|
||||
}
|
||||
p := platforms.Normalize(pl[0])
|
||||
return &p
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/cli/debug"
|
||||
solvererrdefs "github.com/moby/buildkit/solver/errdefs"
|
||||
"github.com/moby/buildkit/sourcepolicy/policysession"
|
||||
"github.com/moby/buildkit/util/grpcerrors"
|
||||
"github.com/moby/buildkit/util/stack"
|
||||
"github.com/pkg/errors"
|
||||
@@ -108,6 +109,11 @@ func main() {
|
||||
if errors.As(err, &exitCodeErr) {
|
||||
os.Exit(int(exitCodeErr))
|
||||
}
|
||||
for _, msg := range policysession.DenyMessages(err) {
|
||||
if msg.GetMessage() != "" {
|
||||
fmt.Fprintf(os.Stderr, "Policy: %s\n", msg.GetMessage())
|
||||
}
|
||||
}
|
||||
|
||||
for _, s := range solvererrdefs.Sources(err) {
|
||||
s.Print(cmd.Err())
|
||||
|
||||
@@ -216,6 +216,7 @@ func runEval(ctx context.Context, dockerCli command.Cli, source string, opts eva
|
||||
Env: env,
|
||||
FS: fsProvider,
|
||||
VerifierProvider: verifier,
|
||||
DefaultPlatform: &p,
|
||||
})
|
||||
|
||||
srcReq := &gwpb.ResolveSourceMetaResponse{
|
||||
@@ -387,6 +388,7 @@ func parseSource(input string) (*pb.SourceOp, error) {
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse image source reference")
|
||||
}
|
||||
ref = reference.TagNameOnly(ref)
|
||||
return &pb.SourceOp{Identifier: "docker-image://" + ref.String()}, nil
|
||||
}
|
||||
if strings.HasPrefix(input, "git://") {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package policy
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
|
||||
"github.com/open-policy-agent/opa/v1/ast"
|
||||
)
|
||||
|
||||
const builtinPolicyModuleFilename = "builtin/buildx_defaults.rego"
|
||||
|
||||
//go:embed builtins.rego
|
||||
var builtinPolicyModule string
|
||||
|
||||
func builtinPolicyModuleAST() (*ast.Module, error) {
|
||||
return ast.ParseModuleWithOpts(builtinPolicyModuleFilename, builtinPolicyModule, ast.ParserOptions{
|
||||
RegoVersion: ast.RegoV1,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package docker
|
||||
|
||||
docker_github_builder(image, repo) if {
|
||||
image.hasProvenance
|
||||
some sig in image.signatures
|
||||
docker_github_builder_signature(sig, repo)
|
||||
}
|
||||
|
||||
docker_github_builder_tag(image, repo, tag) if {
|
||||
docker_github_builder(image, repo)
|
||||
some sig in image.signatures
|
||||
sig.signer.sourceRepositoryRef == sprintf("refs/tags/%s", [tag])
|
||||
}
|
||||
|
||||
docker_github_builder_signature(sig, repo) if {
|
||||
sig.kind == "docker-github-builder"
|
||||
sig.type == "bundle-v0.3"
|
||||
sig.signer.certificateIssuer == "CN=sigstore-intermediate,O=sigstore.dev"
|
||||
sig.signer.issuer == "https://token.actions.githubusercontent.com"
|
||||
sig.signer.sourceRepositoryURI == sprintf("https://github.com/%s", [repo])
|
||||
sig.signer.runnerEnvironment == "github-hosted"
|
||||
count(sig.timestamps) > 0
|
||||
}
|
||||
@@ -89,6 +89,10 @@ func parseSignatures(ctx context.Context, getVerifier PolicyVerifierProvider, ac
|
||||
}
|
||||
desc := toOCIDescriptor(rootBlob.Descriptor_)
|
||||
|
||||
if desc.MediaType != ocispecs.MediaTypeImageIndex {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
sc, err := policyimage.ResolveSignatureChain(ctx, acp, desc, platform)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "resolving signature chain for image %s", desc.Digest)
|
||||
@@ -120,6 +124,8 @@ func parseSignatures(ctx context.Context, getVerifier PolicyVerifierProvider, ac
|
||||
Timestamps: siRaw.Timestamps,
|
||||
IsDHI: siRaw.IsDHI,
|
||||
DockerReference: siRaw.DockerReference,
|
||||
SignatureType: toSignatureType(siRaw.SignatureType),
|
||||
SignatureKind: toSignatureKind(siRaw.Kind),
|
||||
}
|
||||
|
||||
// TODO: signature type after upstream update
|
||||
|
||||
+8
-1
@@ -195,6 +195,13 @@ func loadPolicyModules(root fs.StatFS, filename string) (map[string]*ast.Module,
|
||||
modules := map[string]*ast.Module{
|
||||
filepath.ToSlash(policyFile): mod,
|
||||
}
|
||||
builtinMod, err := builtinPolicyModuleAST()
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "parse builtin policy module %s", builtinPolicyModuleFilename)
|
||||
}
|
||||
if _, ok := modules[builtinPolicyModuleFilename]; !ok {
|
||||
modules[builtinPolicyModuleFilename] = builtinMod
|
||||
}
|
||||
files := []File{
|
||||
{
|
||||
Filename: filepath.ToSlash(policyFile),
|
||||
@@ -593,7 +600,7 @@ func missingInputRefs(mods []*ast.Module, input *Input) []string {
|
||||
return nil
|
||||
}
|
||||
inputMap := normalizeInput(input)
|
||||
refs := collectUnknowns(mods)
|
||||
refs := collectUnknowns(mods, nil)
|
||||
missing := make([]string, 0, len(refs))
|
||||
for _, ref := range refs {
|
||||
key := strings.TrimPrefix(ref, "input.")
|
||||
|
||||
+40
-3
@@ -132,7 +132,8 @@ type Image struct {
|
||||
}
|
||||
|
||||
type AttestationSignature struct {
|
||||
SignatureType SignatureType `json:"signatureType,omitempty"`
|
||||
SignatureKind SignatureKind `json:"kind,omitempty"`
|
||||
SignatureType SignatureType `json:"type,omitempty"`
|
||||
Timestamps []policytypes.TimestampVerificationResult `json:"timestamps,omitempty"`
|
||||
DockerReference string `json:"dockerReference,omitempty"`
|
||||
IsDHI bool `json:"isDHI,omitempty"`
|
||||
@@ -171,10 +172,46 @@ type SignerInfo struct {
|
||||
type SignatureType string
|
||||
|
||||
const (
|
||||
SignatureTypeBundle SignatureType = "bundle-v0.3"
|
||||
SignatureTypeHashedRecord SignatureType = "hashedreckord"
|
||||
SignatureTypeBundleV03 SignatureType = "bundle-v0.3"
|
||||
SignatureTypeSimpleSigningV1 SignatureType = "simplesigning-v1"
|
||||
)
|
||||
|
||||
func toSignatureType(st policytypes.SignatureType) SignatureType {
|
||||
switch st {
|
||||
case policytypes.SignatureBundleV03:
|
||||
return SignatureTypeBundleV03
|
||||
case policytypes.SignatureSimpleSigningV1:
|
||||
return SignatureTypeSimpleSigningV1
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type SignatureKind string
|
||||
|
||||
const (
|
||||
SignatureKindDockerGithubBuilder SignatureKind = "docker-github-builder"
|
||||
SignatureKindDockerHardenedImage SignatureKind = "docker-hardened-image"
|
||||
SignatureKindSelfSignedGithubRepo SignatureKind = "self-signed-github-repo"
|
||||
SignatureKindSelfSigned SignatureKind = "self-signed"
|
||||
SignatureKindUntrusted SignatureKind = "untrusted"
|
||||
)
|
||||
|
||||
func toSignatureKind(k policytypes.Kind) SignatureKind {
|
||||
switch k {
|
||||
case policytypes.KindDockerGithubBuilder:
|
||||
return SignatureKindDockerGithubBuilder
|
||||
case policytypes.KindDockerHardenedImage:
|
||||
return SignatureKindDockerHardenedImage
|
||||
case policytypes.KindSelfSignedGithubRepo:
|
||||
return SignatureKindSelfSignedGithubRepo
|
||||
case policytypes.KindSelfSigned:
|
||||
return SignatureKindSelfSigned
|
||||
case policytypes.KindUntrusted:
|
||||
return SignatureKindUntrusted
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Local struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
+60
-16
@@ -59,6 +59,7 @@ type Opt struct {
|
||||
Log func(logrus.Level, string)
|
||||
FS func() (fs.StatFS, func() error, error)
|
||||
VerifierProvider PolicyVerifierProvider
|
||||
DefaultPlatform *ocispecs.Platform
|
||||
}
|
||||
|
||||
var _ policysession.PolicyCallback = (&Policy{}).CheckPolicy
|
||||
@@ -90,16 +91,13 @@ func (p *Policy) CheckPolicy(ctx context.Context, req *policysession.CheckPolicy
|
||||
src := req.Source
|
||||
var platform *ocispecs.Platform
|
||||
if req.Platform != nil {
|
||||
platformStr := req.Platform.OS + "/" + req.Platform.Architecture
|
||||
if req.Platform.Variant != "" {
|
||||
platformStr += "/" + req.Platform.Variant
|
||||
}
|
||||
pl, err := platforms.Parse(platformStr)
|
||||
pl, err := platformFromReq(req)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "failed to parse platform")
|
||||
return nil, nil, err
|
||||
}
|
||||
pl = platforms.Normalize(pl)
|
||||
platform = &pl
|
||||
platform = pl
|
||||
} else {
|
||||
platform = p.opt.DefaultPlatform
|
||||
}
|
||||
|
||||
inp, unknowns, err := SourceToInputWithLogger(ctx, p.opt.VerifierProvider, src, platform, p.opt.Log)
|
||||
@@ -203,6 +201,7 @@ func (p *Policy) CheckPolicy(ctx context.Context, req *policysession.CheckPolicy
|
||||
opts = append(opts, f.impl(st))
|
||||
}
|
||||
|
||||
opts = append(opts, rego.Module(builtinPolicyModuleFilename, builtinPolicyModule))
|
||||
for _, file := range p.opt.Files {
|
||||
opts = append(opts, rego.Module(file.Filename, string(file.Data)))
|
||||
}
|
||||
@@ -210,7 +209,7 @@ func (p *Policy) CheckPolicy(ctx context.Context, req *policysession.CheckPolicy
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "failed to marshal policy input")
|
||||
}
|
||||
p.log(logrus.InfoLevel, "checking policy for source %s", src.Source.Identifier)
|
||||
p.log(logrus.InfoLevel, "checking policy for source %s", sourceName(req))
|
||||
p.log(logrus.DebugLevel, "policy input: %s", dt)
|
||||
|
||||
if len(unknowns) > 0 {
|
||||
@@ -224,10 +223,11 @@ func (p *Policy) CheckPolicy(ctx context.Context, req *policysession.CheckPolicy
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
unk := collectUnknowns(pq.Support)
|
||||
unk := collectUnknowns(pq.Support, unknowns)
|
||||
if _, ok := st.Unknowns[funcVerifyGitSignature]; ok {
|
||||
unk = append(unk, "input.git.commit")
|
||||
}
|
||||
|
||||
if len(unk) > 0 {
|
||||
next := &gwpb.ResolveSourceMetaRequest{
|
||||
Source: req.Source.Source,
|
||||
@@ -237,7 +237,7 @@ func (p *Policy) CheckPolicy(ctx context.Context, req *policysession.CheckPolicy
|
||||
return nil, nil, err
|
||||
}
|
||||
if next.Image != nil || next.Git != nil || hasHTTPUnknowns(unk) {
|
||||
p.log(logrus.InfoLevel, "policy decision for source %s: resolve missing fields %+v", src.Source.Identifier, summarizeUnknownsForLog(unk))
|
||||
p.log(logrus.InfoLevel, "policy decision for source %s: resolve missing fields %+v", sourceName(req), summarizeUnknownsForLog(unk))
|
||||
return nil, next, nil
|
||||
}
|
||||
}
|
||||
@@ -289,14 +289,14 @@ func (p *Policy) CheckPolicy(ctx context.Context, req *policysession.CheckPolicy
|
||||
|
||||
if resp.Action == moby_buildkit_v1_sourcepolicy.PolicyAction_ALLOW {
|
||||
if len(st.ImagePins) > 1 {
|
||||
return nil, nil, errors.Errorf("multiple image pins set to %s: %v", src.Source.Identifier, st.ImagePins)
|
||||
return nil, nil, errors.Errorf("multiple image pins set to %s: %v", sourceName(req), st.ImagePins)
|
||||
}
|
||||
if len(st.ImagePins) == 1 {
|
||||
newSrc, err := addPinToImage(src.Source, slices.Collect(maps.Keys(st.ImagePins))[0])
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrapf(err, "failed to add image pin to source")
|
||||
}
|
||||
p.log(logrus.InfoLevel, "policy decision for source %s: convert to %s", src.Source.Identifier, newSrc.Identifier)
|
||||
p.log(logrus.InfoLevel, "policy decision for source %s: convert to %s", sourceName(req), newSrc.Identifier)
|
||||
|
||||
return &policysession.DecisionResponse{
|
||||
Action: moby_buildkit_v1_sourcepolicy.PolicyAction_CONVERT,
|
||||
@@ -305,11 +305,38 @@ func (p *Policy) CheckPolicy(ctx context.Context, req *policysession.CheckPolicy
|
||||
}
|
||||
}
|
||||
|
||||
p.log(logrus.InfoLevel, "policy decision for source %s: %s %v", src.Source.Identifier, resp.Action, resp.DenyMessages)
|
||||
p.log(logrus.InfoLevel, "policy decision for source %s: %s", sourceName(req), resp.Action)
|
||||
for _, dm := range resp.DenyMessages {
|
||||
p.log(logrus.InfoLevel, " - %s", dm.Message)
|
||||
}
|
||||
|
||||
return resp, nil, nil
|
||||
}
|
||||
|
||||
func platformFromReq(req *policysession.CheckPolicyRequest) (*ocispecs.Platform, error) {
|
||||
if req.Platform != nil {
|
||||
platformStr := req.Platform.OS + "/" + req.Platform.Architecture
|
||||
if req.Platform.Variant != "" {
|
||||
platformStr += "/" + req.Platform.Variant
|
||||
}
|
||||
pl, err := platforms.Parse(platformStr)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to parse platform")
|
||||
}
|
||||
pl = platforms.Normalize(pl)
|
||||
return &pl, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func sourceName(req *policysession.CheckPolicyRequest) string {
|
||||
name := req.Source.Source.Identifier
|
||||
if p, _ := platformFromReq(req); p != nil {
|
||||
name += " (" + platforms.Format(*p) + ")"
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func (p *Policy) Print(ctx print.Context, msg string) error {
|
||||
if p.opt.Log != nil {
|
||||
p.opt.Log(logrus.InfoLevel, ctx.Location.Format("%s", msg))
|
||||
@@ -646,7 +673,7 @@ func AddUnknownsWithLogger(logf func(logrus.Level, string), req *gwpb.ResolveSou
|
||||
return nil
|
||||
}
|
||||
|
||||
func collectUnknowns(mods []*ast.Module) []string {
|
||||
func collectUnknowns(mods []*ast.Module, allowed []string) []string {
|
||||
seen := map[string]struct{}{}
|
||||
var out []string
|
||||
|
||||
@@ -654,6 +681,7 @@ func collectUnknowns(mods []*ast.Module) []string {
|
||||
ast.WalkRefs(mod, func(ref ast.Ref) bool {
|
||||
if ref.HasPrefix(ast.InputRootRef) {
|
||||
s := ref.String() // e.g. "input.request.path"
|
||||
s = "input." + trimKey(strings.TrimPrefix(s, "input."))
|
||||
if _, ok := seen[s]; !ok {
|
||||
seen[s] = struct{}{}
|
||||
out = append(out, s)
|
||||
@@ -662,7 +690,23 @@ func collectUnknowns(mods []*ast.Module) []string {
|
||||
return true
|
||||
})
|
||||
}
|
||||
return out
|
||||
if allowed == nil {
|
||||
return out
|
||||
}
|
||||
|
||||
valid := map[string]struct{}{}
|
||||
for _, k := range allowed {
|
||||
valid[k] = struct{}{}
|
||||
}
|
||||
|
||||
filtered := make([]string, 0, len(out))
|
||||
for _, k := range out {
|
||||
if _, ok := valid[k]; ok {
|
||||
filtered = append(filtered, k)
|
||||
}
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
func summarizeUnknownsForLog(unk []string) []string {
|
||||
|
||||
@@ -12,6 +12,7 @@ var policyTestTests = []func(t *testing.T, sb integration.Sandbox){
|
||||
testPolicyTestRunFilter,
|
||||
testPolicyTestFailMissingInput,
|
||||
testPolicyTestNestedPath,
|
||||
testPolicyTestDockerGitHubBuilder,
|
||||
}
|
||||
|
||||
func testPolicyTestRunFilter(t *testing.T, sb integration.Sandbox) {
|
||||
@@ -182,3 +183,128 @@ test_allowlisted_repo if {
|
||||
require.NoError(t, err, string(out))
|
||||
require.Contains(t, string(out), "test_allowlisted_repo: PASS")
|
||||
}
|
||||
|
||||
func testPolicyTestDockerGitHubBuilder(t *testing.T, sb integration.Sandbox) {
|
||||
skipNoCompatBuildKit(t, sb, ">= 0.26.0-0", "policy input requires BuildKit v0.26.0+")
|
||||
dir := tmpdir(
|
||||
t,
|
||||
fstest.CreateFile("policy.rego", []byte(`
|
||||
package docker
|
||||
|
||||
default allow = false
|
||||
|
||||
allow if docker_github_builder(input.image, "org/repo")
|
||||
|
||||
decision := {"allow": allow}
|
||||
`), 0600),
|
||||
fstest.CreateFile("policy_test.rego", []byte(`
|
||||
package docker
|
||||
|
||||
test_docker_github_builder if {
|
||||
result := data.docker.decision with input as {
|
||||
"image": {
|
||||
"hasProvenance": true,
|
||||
"signatures": [{
|
||||
"kind": "docker-github-builder",
|
||||
"type": "bundle-v0.3",
|
||||
"signer": {
|
||||
"certificateIssuer": "CN=sigstore-intermediate,O=sigstore.dev",
|
||||
"issuer": "https://token.actions.githubusercontent.com",
|
||||
"sourceRepositoryURI": "https://github.com/org/repo",
|
||||
"runnerEnvironment": "github-hosted"
|
||||
},
|
||||
"timestamps": [{
|
||||
"type": "tlog",
|
||||
"uri": "https://example.com/tlog",
|
||||
"timestamp": "2024-01-01T00:00:00Z"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
result.allow
|
||||
}
|
||||
|
||||
test_docker_github_builder_denied if {
|
||||
result := data.docker.decision with input as {
|
||||
"image": {
|
||||
"hasProvenance": true,
|
||||
"signatures": [{
|
||||
"kind": "docker-github-builder",
|
||||
"type": "bundle-v0.3",
|
||||
"signer": {
|
||||
"certificateIssuer": "CN=sigstore-intermediate,O=sigstore.dev",
|
||||
"issuer": "https://token.actions.githubusercontent.com",
|
||||
"sourceRepositoryURI": "https://github.com/other/repo",
|
||||
"runnerEnvironment": "github-hosted"
|
||||
},
|
||||
"timestamps": [{
|
||||
"type": "tlog",
|
||||
"uri": "https://example.com/tlog",
|
||||
"timestamp": "2024-01-01T00:00:00Z"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
not result.allow
|
||||
}
|
||||
|
||||
test_docker_github_builder_tag if {
|
||||
docker_github_builder_tag({
|
||||
"hasProvenance": true,
|
||||
"signatures": [{
|
||||
"kind": "docker-github-builder",
|
||||
"type": "bundle-v0.3",
|
||||
"signer": {
|
||||
"certificateIssuer": "CN=sigstore-intermediate,O=sigstore.dev",
|
||||
"issuer": "https://token.actions.githubusercontent.com",
|
||||
"sourceRepositoryURI": "https://github.com/org/repo",
|
||||
"sourceRepositoryRef": "refs/tags/v1.2.3",
|
||||
"runnerEnvironment": "github-hosted"
|
||||
},
|
||||
"timestamps": [{
|
||||
"type": "tlog",
|
||||
"uri": "https://example.com/tlog",
|
||||
"timestamp": "2024-01-01T00:00:00Z"
|
||||
}]
|
||||
}]
|
||||
}, "org/repo", "v1.2.3")
|
||||
}
|
||||
|
||||
test_docker_github_builder_tag_denied if {
|
||||
not docker_github_builder_tag({
|
||||
"hasProvenance": true,
|
||||
"signatures": [{
|
||||
"kind": "docker-github-builder",
|
||||
"type": "bundle-v0.3",
|
||||
"signer": {
|
||||
"certificateIssuer": "CN=sigstore-intermediate,O=sigstore.dev",
|
||||
"issuer": "https://token.actions.githubusercontent.com",
|
||||
"sourceRepositoryURI": "https://github.com/org/repo",
|
||||
"sourceRepositoryRef": "refs/tags/other",
|
||||
"runnerEnvironment": "github-hosted"
|
||||
},
|
||||
"timestamps": [{
|
||||
"type": "tlog",
|
||||
"uri": "https://example.com/tlog",
|
||||
"timestamp": "2024-01-01T00:00:00Z"
|
||||
}]
|
||||
}]
|
||||
}, "org/repo", "v1.2.3")
|
||||
}
|
||||
`), 0600),
|
||||
)
|
||||
|
||||
cmd := buildxCmd(sb, withDir(dir), withArgs(
|
||||
"policy",
|
||||
"test",
|
||||
"--filename",
|
||||
"policy",
|
||||
".",
|
||||
))
|
||||
out, err := cmd.CombinedOutput()
|
||||
require.NoError(t, err, string(out))
|
||||
require.Contains(t, string(out), "test_docker_github_builder: PASS")
|
||||
require.Contains(t, string(out), "test_docker_github_builder_denied: PASS")
|
||||
require.Contains(t, string(out), "test_docker_github_builder_tag: PASS")
|
||||
require.Contains(t, string(out), "test_docker_github_builder_tag_denied: PASS")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user