mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 10:03:42 +08:00 
			
		
		
		
	full diff: - https://github.com/moby/buildkit/compare/20230620112432...v0.12.0 - https://github.com/moby/buildkit/compare/v0.12.0...faa0cc7da3536923d85b74b2bb2d13c12a6ecc99 Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
		
			
				
	
	
		
			162 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			162 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package sourcepolicy
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
 | 
						|
	"github.com/moby/buildkit/solver/pb"
 | 
						|
	spb "github.com/moby/buildkit/sourcepolicy/pb"
 | 
						|
	"github.com/moby/buildkit/util/bklog"
 | 
						|
	"github.com/pkg/errors"
 | 
						|
	"github.com/sirupsen/logrus"
 | 
						|
)
 | 
						|
 | 
						|
var (
 | 
						|
	// ErrSourceDenied is returned by the policy engine when a source is denied by the policy.
 | 
						|
	ErrSourceDenied = errors.New("source denied by policy")
 | 
						|
 | 
						|
	// ErrTooManyOps is returned by the policy engine when there are too many converts for a single source op.
 | 
						|
	ErrTooManyOps = errors.New("too many operations")
 | 
						|
)
 | 
						|
 | 
						|
// Engine is the source policy engine.
 | 
						|
// It is responsible for evaluating a source policy against a source operation.
 | 
						|
// Create one with `NewEngine`
 | 
						|
//
 | 
						|
// Rule matching is delegated to the `Matcher` interface.
 | 
						|
// Mutations are delegated to the `Mutater` interface.
 | 
						|
type Engine struct {
 | 
						|
	pol     []*spb.Policy
 | 
						|
	sources map[string]*selectorCache
 | 
						|
}
 | 
						|
 | 
						|
// NewEngine creates a new source policy engine.
 | 
						|
func NewEngine(pol []*spb.Policy) *Engine {
 | 
						|
	return &Engine{
 | 
						|
		pol: pol,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// TODO: The key here can't be used to cache attr constraint regexes.
 | 
						|
func (e *Engine) selectorCache(src *spb.Selector) *selectorCache {
 | 
						|
	if e.sources == nil {
 | 
						|
		e.sources = map[string]*selectorCache{}
 | 
						|
	}
 | 
						|
 | 
						|
	key := src.MatchType.String() + " " + src.Identifier
 | 
						|
 | 
						|
	if s, ok := e.sources[key]; ok {
 | 
						|
		return s
 | 
						|
	}
 | 
						|
 | 
						|
	s := &selectorCache{Selector: src}
 | 
						|
 | 
						|
	e.sources[key] = s
 | 
						|
	return s
 | 
						|
}
 | 
						|
 | 
						|
// Evaluate evaluates a source operation against the policy.
 | 
						|
//
 | 
						|
// Policies are re-evaluated for each convert rule.
 | 
						|
// Evaluate will error if the there are too many converts for a single source op to prevent infinite loops.
 | 
						|
// This function may error out even if the op was mutated, in which case `true` will be returned along with the error.
 | 
						|
//
 | 
						|
// An error is returned when the source is denied by the policy.
 | 
						|
func (e *Engine) Evaluate(ctx context.Context, op *pb.Op) (bool, error) {
 | 
						|
	if len(e.pol) == 0 {
 | 
						|
		return false, nil
 | 
						|
	}
 | 
						|
 | 
						|
	var mutated bool
 | 
						|
	const maxIterr = 20
 | 
						|
 | 
						|
	for i := 0; ; i++ {
 | 
						|
		if i > maxIterr {
 | 
						|
			return mutated, errors.Wrapf(ErrTooManyOps, "too many mutations on a single source")
 | 
						|
		}
 | 
						|
 | 
						|
		srcOp := op.GetSource()
 | 
						|
		if srcOp == nil {
 | 
						|
			return false, nil
 | 
						|
		}
 | 
						|
		if i == 0 {
 | 
						|
			ctx = bklog.WithLogger(ctx, bklog.G(ctx).WithField("orig", *srcOp).WithField("updated", op.GetSource()))
 | 
						|
		}
 | 
						|
 | 
						|
		mut, err := e.evaluatePolicies(ctx, srcOp)
 | 
						|
		if mut {
 | 
						|
			mutated = true
 | 
						|
		}
 | 
						|
		if err != nil {
 | 
						|
			return mutated, err
 | 
						|
		}
 | 
						|
		if !mut {
 | 
						|
			break
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return mutated, nil
 | 
						|
}
 | 
						|
 | 
						|
func (e *Engine) evaluatePolicies(ctx context.Context, srcOp *pb.SourceOp) (bool, error) {
 | 
						|
	for _, pol := range e.pol {
 | 
						|
		mut, err := e.evaluatePolicy(ctx, pol, srcOp)
 | 
						|
		if mut || err != nil {
 | 
						|
			return mut, err
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false, nil
 | 
						|
}
 | 
						|
 | 
						|
// evaluatePolicy evaluates a single policy against a source operation.
 | 
						|
// If the source is mutated the policy is short-circuited and `true` is returned.
 | 
						|
// If the source is denied, an error will be returned.
 | 
						|
//
 | 
						|
// For Allow/Deny rules, the last matching rule wins.
 | 
						|
// E.g. `ALLOW foo; DENY foo` will deny `foo`, `DENY foo; ALLOW foo` will allow `foo`.
 | 
						|
func (e *Engine) evaluatePolicy(ctx context.Context, pol *spb.Policy, srcOp *pb.SourceOp) (retMut bool, retErr error) {
 | 
						|
	ident := srcOp.GetIdentifier()
 | 
						|
 | 
						|
	ctx = bklog.WithLogger(ctx, bklog.G(ctx).WithField("ref", ident))
 | 
						|
	defer func() {
 | 
						|
		if retMut || retErr != nil {
 | 
						|
			bklog.G(ctx).WithFields(
 | 
						|
				logrus.Fields{
 | 
						|
					"mutated":       retMut,
 | 
						|
					"updated":       srcOp.GetIdentifier(),
 | 
						|
					logrus.ErrorKey: retErr,
 | 
						|
				}).Debug("Evaluated source policy")
 | 
						|
		}
 | 
						|
	}()
 | 
						|
 | 
						|
	var deny bool
 | 
						|
	for _, rule := range pol.Rules {
 | 
						|
		selector := e.selectorCache(rule.Selector)
 | 
						|
		matched, err := match(ctx, selector, ident, srcOp.Attrs)
 | 
						|
		if err != nil {
 | 
						|
			return false, errors.Wrap(err, "error matching source policy")
 | 
						|
		}
 | 
						|
		if !matched {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		switch rule.Action {
 | 
						|
		case spb.PolicyAction_ALLOW:
 | 
						|
			deny = false
 | 
						|
		case spb.PolicyAction_DENY:
 | 
						|
			deny = true
 | 
						|
		case spb.PolicyAction_CONVERT:
 | 
						|
			mut, err := mutate(ctx, srcOp, rule, selector, ident)
 | 
						|
			if err != nil || mut {
 | 
						|
				return mut, errors.Wrap(err, "error mutating source policy")
 | 
						|
			}
 | 
						|
		default:
 | 
						|
			return false, errors.Errorf("source policy: rule %s %s: unknown type %q", rule.Action, rule.Selector.Identifier, ident)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if deny {
 | 
						|
		return false, errors.Wrapf(ErrSourceDenied, "source %q denied by policy", ident)
 | 
						|
	}
 | 
						|
	return false, nil
 | 
						|
}
 |