Merge pull request #10117 from owncloud/dependabot/go_modules/github.com/open-policy-agent/opa-0.68.0

chore(deps): bump github.com/open-policy-agent/opa from 0.67.1 to 0.68.0
This commit is contained in:
Florian Schade
2024-09-20 09:28:37 +02:00
committed by GitHub
21 changed files with 5256 additions and 52 deletions

2
go.mod
View File

@@ -69,7 +69,7 @@ require (
github.com/onsi/ginkgo v1.16.5
github.com/onsi/ginkgo/v2 v2.20.2
github.com/onsi/gomega v1.34.1
github.com/open-policy-agent/opa v0.67.1
github.com/open-policy-agent/opa v0.68.0
github.com/orcaman/concurrent-map v1.0.0
github.com/owncloud/libre-graph-api-go v1.0.5-0.20240820135012-5fac8096ce9c
github.com/pkg/errors v0.9.1

4
go.sum
View File

@@ -928,8 +928,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/open-policy-agent/opa v0.67.1 h1:rzy26J6g1X+CKknAcx0Vfbt41KqjuSzx4E0A8DAZf3E=
github.com/open-policy-agent/opa v0.67.1/go.mod h1:aqKlHc8E2VAAylYE9x09zJYr/fYzGX+JKne89UGqFzk=
github.com/open-policy-agent/opa v0.68.0 h1:Jl3U2vXRjwk7JrHmS19U3HZO5qxQRinQbJ2eCJYSqJQ=
github.com/open-policy-agent/opa v0.68.0/go.mod h1:5E5SvaPwTpwt2WM177I9Z3eT7qUpmOGjk1ZdHs+TZ4w=
github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg=
github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=

View File

@@ -417,7 +417,7 @@ func (a *Annotations) Copy(node Node) *Annotations {
return &cpy
}
// toObject constructs an AST Object from a.
// toObject constructs an AST Object from the annotation.
func (a *Annotations) toObject() (*Object, *Error) {
obj := NewObject()
@@ -556,7 +556,11 @@ func attachAnnotationsNodes(mod *Module) Errors {
if a.Scope == "" {
switch a.node.(type) {
case *Rule:
a.Scope = annotationScopeRule
if a.Entrypoint {
a.Scope = annotationScopeDocument
} else {
a.Scope = annotationScopeRule
}
case *Package:
a.Scope = annotationScopePackage
case *Import:
@@ -596,8 +600,9 @@ func validateAnnotationScopeAttachment(a *Annotations) *Error {
}
func validateAnnotationEntrypointAttachment(a *Annotations) *Error {
if a.Entrypoint && !(a.Scope == annotationScopeRule || a.Scope == annotationScopePackage) {
return NewError(ParseErr, a.Loc(), "annotation entrypoint applied to non-rule or package scope '%v'", a.Scope)
if a.Entrypoint && !(a.Scope == annotationScopeDocument || a.Scope == annotationScopePackage) {
return NewError(
ParseErr, a.Loc(), "annotation entrypoint applied to non-document or package scope '%v'", a.Scope)
}
return nil
}

View File

@@ -1181,7 +1181,7 @@ var Split = &Builtin{
types.Named("x", types.S).Description("string that is split"),
types.Named("delimiter", types.S).Description("delimiter used for splitting"),
),
types.Named("ys", types.NewArray(nil, types.S)).Description("splitted parts"),
types.Named("ys", types.NewArray(nil, types.S)).Description("split parts"),
),
Categories: stringsCat,
}
@@ -1247,7 +1247,7 @@ var Trim = &Builtin{
var TrimLeft = &Builtin{
Name: "trim_left",
Description: "Returns `value` with all leading instances of the `cutset` chartacters removed.",
Description: "Returns `value` with all leading instances of the `cutset` characters removed.",
Decl: types.NewFunction(
types.Args(
types.Named("value", types.S).Description("string to trim"),
@@ -1273,7 +1273,7 @@ var TrimPrefix = &Builtin{
var TrimRight = &Builtin{
Name: "trim_right",
Description: "Returns `value` with all trailing instances of the `cutset` chartacters removed.",
Description: "Returns `value` with all trailing instances of the `cutset` characters removed.",
Decl: types.NewFunction(
types.Args(
types.Named("value", types.S).Description("string to trim"),
@@ -1356,7 +1356,7 @@ var RenderTemplate = &Builtin{
// Marked non-deterministic because it relies on RNG internally.
var RandIntn = &Builtin{
Name: "rand.intn",
Description: "Returns a random integer between `0` and `n` (`n` exlusive). If `n` is `0`, then `y` is always `0`. For any given argument pair (`str`, `n`), the output will be consistent throughout a query evaluation.",
Description: "Returns a random integer between `0` and `n` (`n` exclusive). If `n` is `0`, then `y` is always `0`. For any given argument pair (`str`, `n`), the output will be consistent throughout a query evaluation.",
Decl: types.NewFunction(
types.Args(
types.Named("str", types.S),
@@ -1750,7 +1750,7 @@ var JSONUnmarshal = &Builtin{
types.Args(
types.Named("x", types.S).Description("a JSON string"),
),
types.Named("y", types.A).Description("the term deseralized from `x`"),
types.Named("y", types.A).Description("the term deserialized from `x`"),
),
Categories: encoding,
}
@@ -1914,7 +1914,7 @@ var YAMLUnmarshal = &Builtin{
types.Args(
types.Named("x", types.S).Description("a YAML string"),
),
types.Named("y", types.A).Description("the term deseralized from `x`"),
types.Named("y", types.A).Description("the term deserialized from `x`"),
),
Categories: encoding,
}
@@ -1951,7 +1951,7 @@ var HexDecode = &Builtin{
types.Args(
types.Named("x", types.S).Description("a hex-encoded string"),
),
types.Named("y", types.S).Description("deseralized from `x`"),
types.Named("y", types.S).Description("deserialized from `x`"),
),
Categories: encoding,
}

View File

@@ -60,7 +60,10 @@ func (tc *typeChecker) copy() *typeChecker {
WithVarRewriter(tc.varRewriter).
WithSchemaSet(tc.ss).
WithAllowNet(tc.allowNet).
WithInputType(tc.input)
WithInputType(tc.input).
WithAllowUndefinedFunctionCalls(tc.allowUndefinedFuncs).
WithBuiltins(tc.builtins).
WithRequiredCapabilities(tc.required)
}
func (tc *typeChecker) WithRequiredCapabilities(c *Capabilities) *typeChecker {

View File

@@ -494,7 +494,7 @@ func (node *trieNode) String() string {
func (node *trieNode) append(prio [2]int, rule *Rule) {
node.rules = append(node.rules, &ruleNode{prio, rule})
if node.values != nil {
if node.values != nil && rule.Head.Value != nil {
node.values.Add(rule.Head.Value)
return
}

View File

@@ -50,6 +50,19 @@ func (v RegoVersion) Int() int {
return 0
}
func (v RegoVersion) String() string {
switch v {
case RegoV0:
return "v0"
case RegoV1:
return "v1"
case RegoV0CompatV1:
return "v0v1"
default:
return "unknown"
}
}
func RegoVersionFromInt(i int) RegoVersion {
if i == 1 {
return RegoV1
@@ -596,7 +609,12 @@ func (p *Parser) parseImport() *Import {
path := imp.Path.Value.(Ref)
if !RootDocumentNames.Contains(path[0]) && !FutureRootDocument.Equal(path[0]) && !RegoRootDocument.Equal(path[0]) {
switch {
case RootDocumentNames.Contains(path[0]):
case FutureRootDocument.Equal(path[0]):
case RegoRootDocument.Equal(path[0]):
default:
p.hint("if this is unexpected, try updating OPA")
p.errorf(imp.Path.Location, "unexpected import path, must begin with one of: %v, got: %v",
RootDocumentNames.Union(NewSet(FutureRootDocument, RegoRootDocument)),
path[0])

View File

@@ -570,7 +570,7 @@ func (pkg *Package) MarshalJSON() ([]byte, error) {
}
// IsValidImportPath returns an error indicating if the import path is invalid.
// If the import path is invalid, err is nil.
// If the import path is valid, err is nil.
func IsValidImportPath(v Value) (err error) {
switch v := v.(type) {
case Var:
@@ -1034,16 +1034,22 @@ func (head *Head) setJSONOptions(opts astJSON.Options) {
func (head *Head) MarshalJSON() ([]byte, error) {
var loc *Location
if head.jsonOptions.MarshalOptions.IncludeLocation.Head {
includeLoc := head.jsonOptions.MarshalOptions.IncludeLocation
if includeLoc.Head {
if head.Location != nil {
loc = head.Location
}
for _, term := range head.Reference {
if term.Location != nil {
term.jsonOptions.MarshalOptions.IncludeLocation.Term = includeLoc.Term
}
}
}
// NOTE(sr): we do this to override the rendering of `head.Reference`.
// It's still what'll be used via the default means of encoding/json
// for unmarshaling a json object into a Head struct!
// NOTE(charlieegan3): we also need to optionally include the location
type h Head
return json.Marshal(struct {
h

File diff suppressed because it is too large Load Diff

View File

@@ -28,6 +28,9 @@ type Opts struct {
// RegoVersion is the version of Rego to format code for.
RegoVersion ast.RegoVersion
// ParserOptions is the parser options used when parsing the module to be formatted.
ParserOptions *ast.ParserOptions
}
// defaultLocationFile is the file name used in `Ast()` for terms
@@ -43,11 +46,15 @@ func Source(filename string, src []byte) ([]byte, error) {
}
func SourceWithOpts(filename string, src []byte, opts Opts) ([]byte, error) {
parserOpts := ast.ParserOptions{}
if opts.RegoVersion == ast.RegoV1 {
// If the rego version is V1, wee need to parse it as such, to allow for future keywords not being imported.
// Otherwise, we'll default to RegoV0
parserOpts.RegoVersion = ast.RegoV1
var parserOpts ast.ParserOptions
if opts.ParserOptions != nil {
parserOpts = *opts.ParserOptions
} else {
if opts.RegoVersion == ast.RegoV1 {
// If the rego version is V1, we need to parse it as such, to allow for future keywords not being imported.
// Otherwise, we'll default to RegoV0
parserOpts.RegoVersion = ast.RegoV1
}
}
module, err := ast.ParseModuleWithOpts(filename, string(src), parserOpts)

View File

@@ -247,6 +247,10 @@ func (fl fileLoader) AsBundle(path string) (*bundle.Bundle, error) {
return nil, err
}
if err := checkForUNCPath(path); err != nil {
return nil, err
}
var bundleLoader bundle.DirectoryLoader
var isDir bool
if fl.reader != nil {
@@ -254,6 +258,7 @@ func (fl fileLoader) AsBundle(path string) (*bundle.Bundle, error) {
} else {
bundleLoader, isDir, err = GetBundleDirectoryLoaderFS(fl.fsys, path, fl.filter)
}
if err != nil {
return nil, err
}
@@ -303,6 +308,10 @@ func GetBundleDirectoryLoaderFS(fsys fs.FS, path string, filter Filter) (bundle.
return nil, false, err
}
if err := checkForUNCPath(path); err != nil {
return nil, false, err
}
var fi fs.FileInfo
if fsys != nil {
fi, err = fs.Stat(fsys, path)
@@ -663,12 +672,18 @@ func allRec(fsys fs.FS, path string, filter Filter, errors *Errors, loaded *Resu
return
}
if err := checkForUNCPath(path); err != nil {
errors.add(err)
return
}
var info fs.FileInfo
if fsys != nil {
info, err = fs.Stat(fsys, path)
} else {
info, err = os.Stat(path)
}
if err != nil {
errors.add(err)
return
@@ -804,3 +819,19 @@ func makeDir(path []string, x interface{}) (map[string]interface{}, bool) {
}
return makeDir(path[:len(path)-1], map[string]interface{}{path[len(path)-1]: x})
}
// isUNC reports whether path is a UNC path.
func isUNC(path string) bool {
return len(path) > 1 && isSlash(path[0]) && isSlash(path[1])
}
func isSlash(c uint8) bool {
return c == '\\' || c == '/'
}
func checkForUNCPath(path string) error {
if isUNC(path) {
return fmt.Errorf("UNC path read is not allowed: %s", path)
}
return nil
}

View File

@@ -243,6 +243,17 @@ func FromContext(ctx context.Context) (*RequestContext, bool) {
return requestContext, ok
}
const httpReqCtxKey = requestContextKey("http-request-context-key")
func WithHTTPRequestContext(parent context.Context, val *HTTPRequestContext) context.Context {
return context.WithValue(parent, httpReqCtxKey, val)
}
func HTTPRequestContextFromContext(ctx context.Context) (*HTTPRequestContext, bool) {
requestContext, ok := ctx.Value(httpReqCtxKey).(*HTTPRequestContext)
return requestContext, ok
}
const decisionCtxKey = requestContextKey("decision_id")
func WithDecisionID(parent context.Context, id string) context.Context {

View File

@@ -30,8 +30,10 @@ const (
ec2DefaultTokenPath = "http://169.254.169.254/latest/api/token"
// ref. https://docs.aws.amazon.com/AmazonECS/latest/userguide/task-iam-roles.html
ecsDefaultCredServicePath = "http://169.254.170.2"
ecsRelativePathEnvVar = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
ecsDefaultCredServicePath = "http://169.254.170.2"
ecsRelativePathEnvVar = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"
ecsFullPathEnvVar = "AWS_CONTAINER_CREDENTIALS_FULL_URI"
ecsAuthorizationTokenEnvVar = "AWS_CONTAINER_AUTHORIZATION_TOKEN"
// ref. https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html
stsDefaultDomain = "amazonaws.com"
@@ -211,7 +213,12 @@ func (cs *awsMetadataCredentialService) urlForMetadataService() (string, error)
// otherwise, check environment to see if it looks like we're in an ECS
// container (with implied role association)
if isECS() {
return ecsDefaultCredServicePath + os.Getenv(ecsRelativePathEnvVar), nil
// first check if the relative env var exists; if so we use that otherwise we
// use the "full" var
if _, relativeExists := os.LookupEnv(ecsRelativePathEnvVar); relativeExists {
return ecsDefaultCredServicePath + os.Getenv(ecsRelativePathEnvVar), nil
}
return os.Getenv(ecsFullPathEnvVar), nil
}
// if there's no role name and we don't appear to have a path to the
// ECS container service, then the configuration is invalid
@@ -267,6 +274,16 @@ func (cs *awsMetadataCredentialService) refreshFromService(ctx context.Context)
return errors.New("unable to construct metadata HTTP request: " + err.Error())
}
// if using the AWS_CONTAINER_CREDENTIALS_FULL_URI variable, we need to associate the token
// to the request
if _, useFullPath := os.LookupEnv(ecsFullPathEnvVar); useFullPath {
token, tokenExists := os.LookupEnv(ecsAuthorizationTokenEnvVar)
if !tokenExists {
return errors.New("unable to get ECS metadata authorization token")
}
req.Header.Set("Authorization", token)
}
// if in the EC2 environment, we will use IMDSv2, which requires a session cookie from a
// PUT request on the token endpoint before it will give the credentials, this provides
// protection from SSRF attacks
@@ -604,8 +621,9 @@ func (cs *awsWebIdentityCredentialService) credentials(ctx context.Context) (aws
func isECS() bool {
// the special relative path URI is set by the container agent in the ECS environment only
_, isECS := os.LookupEnv(ecsRelativePathEnvVar)
return isECS
_, isECSRelative := os.LookupEnv(ecsRelativePathEnvVar)
_, isECSFull := os.LookupEnv(ecsFullPathEnvVar)
return isECSRelative || isECSFull
}
// ecrAuthPlugin authorizes requests to AWS ECR.

View File

@@ -124,6 +124,7 @@ type EvalContext struct {
printHook print.Hook
capabilities *ast.Capabilities
strictBuiltinErrors bool
virtualCache topdown.VirtualCache
}
func (e *EvalContext) RawInput() *interface{} {
@@ -342,6 +343,14 @@ func EvalPrintHook(ph print.Hook) EvalOption {
}
}
// EvalVirtualCache sets the topdown.VirtualCache to use for evaluation. This is
// optional, and if not set, the default cache is used.
func EvalVirtualCache(vc topdown.VirtualCache) EvalOption {
return func(e *EvalContext) {
e.virtualCache = vc
}
}
func (pq preparedQuery) Modules() map[string]*ast.Module {
mods := make(map[string]*ast.Module)
@@ -2101,7 +2110,8 @@ func (r *Rego) eval(ctx context.Context, ectx *EvalContext) (ResultSet, error) {
WithBuiltinErrorList(r.builtinErrorList).
WithSeed(ectx.seed).
WithPrintHook(ectx.printHook).
WithDistributedTracingOpts(r.distributedTacingOpts)
WithDistributedTracingOpts(r.distributedTacingOpts).
WithVirtualCache(ectx.virtualCache)
if !ectx.time.IsZero() {
q = q.WithTime(ectx.time)

View File

@@ -9,6 +9,29 @@ import (
"github.com/open-policy-agent/opa/util"
)
// VirtualCache defines the interface for a cache that stores the results of
// evaluated virtual documents (rules).
// The cache is a stack of frames, where each frame is a mapping from references
// to values.
type VirtualCache interface {
// Push pushes a new, empty frame of value mappings onto the stack.
Push()
// Pop pops the top frame of value mappings from the stack, removing all associated entries.
Pop()
// Get returns the value associated with the given reference. The second return value
// indicates whether the reference has a recorded 'undefined' result.
Get(ref ast.Ref) (*ast.Term, bool)
// Put associates the given reference with the given value. If the value is nil, the reference
// is marked as having an 'undefined' result.
Put(ref ast.Ref, value *ast.Term)
// Keys returns the set of keys that have been cached for the active frame.
Keys() []ast.Ref
}
type virtualCache struct {
stack []*virtualCacheElem
}
@@ -19,7 +42,7 @@ type virtualCacheElem struct {
undefined bool
}
func newVirtualCache() *virtualCache {
func NewVirtualCache() VirtualCache {
cache := &virtualCache{}
cache.Push()
return cache
@@ -77,6 +100,26 @@ func (c *virtualCache) Put(ref ast.Ref, value *ast.Term) {
}
}
func (c *virtualCache) Keys() []ast.Ref {
node := c.stack[len(c.stack)-1]
return keysRecursive(nil, node)
}
func keysRecursive(root ast.Ref, node *virtualCacheElem) []ast.Ref {
var keys []ast.Ref
node.children.Iter(func(k, v util.T) bool {
ref := root.Append(k.(*ast.Term))
if v.(*virtualCacheElem).value != nil {
keys = append(keys, ref)
}
if v.(*virtualCacheElem).children.Len() > 0 {
keys = append(keys, keysRecursive(ref, v.(*virtualCacheElem))...)
}
return false
})
return keys
}
func newVirtualCacheElem() *virtualCacheElem {
return &virtualCacheElem{children: newVirtualCacheHashMap()}
}

View File

@@ -90,7 +90,7 @@ type eval struct {
builtinCache builtins.Cache
ndBuiltinCache builtins.NDBCache
functionMocks *functionMocksStack
virtualCache *virtualCache
virtualCache VirtualCache
comprehensionCache *comprehensionCache
interQueryBuiltinCache cache.InterQueryCache
saveSet *saveSet
@@ -2407,6 +2407,15 @@ type evalVirtualPartialCacheHint struct {
full bool
}
func (h *evalVirtualPartialCacheHint) keyWithoutScope() ast.Ref {
if h.key != nil {
if _, ok := h.key[len(h.key)-1].Value.(vcKeyScope); ok {
return h.key[:len(h.key)-1]
}
}
return h.key
}
func (e evalVirtualPartial) eval(iter unifyIterator) error {
unknown := e.e.unknown(e.ref[:e.pos+1], e.bindings)
@@ -2485,7 +2494,7 @@ func (e evalVirtualPartial) evalEachRule(iter unifyIterator, unknown bool) error
}
if hint.key != nil {
if v, err := result.Value.Find(hint.key[e.pos+1:]); err == nil && v != nil {
if v, err := result.Value.Find(hint.keyWithoutScope()[e.pos+1:]); err == nil && v != nil {
e.e.virtualCache.Put(hint.key, ast.NewTerm(v))
}
}
@@ -2832,6 +2841,8 @@ func (e evalVirtualPartial) evalCache(iter unifyIterator) (evalVirtualPartialCac
plugged := e.bindings.Plug(e.ref[e.pos+1])
if _, ok := plugged.Value.(ast.Var); ok {
// Note: we might have additional opportunity to optimize here, if we consider that ground values
// right of e.pos could create a smaller eval "scope" through ref bi-unification before evaluating rules.
hint.full = true
hint.key = e.plugged[:e.pos+1]
e.e.instr.counterIncr(evalOpVirtualCacheMiss)
@@ -2840,19 +2851,76 @@ func (e evalVirtualPartial) evalCache(iter unifyIterator) (evalVirtualPartialCac
m := maxRefLength(e.ir.Rules, len(e.ref))
// Creating the hint key by walking the ref and plugging vars until we hit a non-ground term.
// Any ground term right of this point will affect the scope of evaluation by ref unification,
// so we create a virtual-cache scope key to qualify the result stored in the cache.
//
// E.g. given the following rule:
//
// package example
//
// a[x][y][z] := x + y + z if {
// some x in [1, 2]
// some y in [3, 4]
// some z in [5, 6]
// }
//
// and the following ref (1):
//
// data.example.a[1][_][5]
//
// then the hint key will be:
//
// data.example.a[1][<_,5>]
//
// where <_,5> is the scope of the pre-eval unification.
// This part does not contribute to the "location" of the cached data.
//
// The following ref (2):
//
// data.example.a[1][_][6]
//
// will produce the same hint key "location" 'data.example.a[1]', but a different scope component
// '<_,6>', which will create a different entry in the cache.
scoping := false
hintKeyEnd := 0
for i := e.pos + 1; i < m; i++ {
plugged = e.bindings.Plug(e.ref[i])
if !plugged.IsGround() {
break
if plugged.IsGround() && !scoping {
hintKeyEnd = i
hint.key = append(e.plugged[:i], plugged)
} else {
scoping = true
hl := len(hint.key)
if hl == 0 {
break
}
if scope, ok := hint.key[hl-1].Value.(vcKeyScope); ok {
scope.Ref = append(scope.Ref, plugged)
hint.key[len(hint.key)-1] = ast.NewTerm(scope)
} else {
scope = vcKeyScope{}
scope.Ref = append(scope.Ref, plugged)
hint.key = append(hint.key, ast.NewTerm(scope))
}
}
hint.key = append(e.plugged[:i], plugged)
if cached, _ := e.e.virtualCache.Get(hint.key); cached != nil {
e.e.instr.counterIncr(evalOpVirtualCacheHit)
hint.hit = true
return hint, e.evalTerm(iter, i+1, cached, e.bindings)
return hint, e.evalTerm(iter, hintKeyEnd+1, cached, e.bindings)
}
}
if hl := len(hint.key); hl > 0 {
if scope, ok := hint.key[hl-1].Value.(vcKeyScope); ok {
scope = scope.reduce()
if scope.empty() {
hint.key = hint.key[:hl-1]
} else {
hint.key[hl-1].Value = scope
}
}
}
@@ -2861,6 +2929,85 @@ func (e evalVirtualPartial) evalCache(iter unifyIterator) (evalVirtualPartialCac
return hint, nil
}
// vcKeyScope represents the scoping that pre-rule-eval ref unification imposes on a virtual cache entry.
type vcKeyScope struct {
ast.Ref
}
func (q vcKeyScope) Compare(other ast.Value) int {
if q2, ok := other.(vcKeyScope); ok {
r1 := q.Ref
r2 := q2.Ref
if len(r1) != len(r2) {
return -1
}
for i := range r1 {
_, v1IsVar := r1[i].Value.(ast.Var)
_, v2IsVar := r2[i].Value.(ast.Var)
if v1IsVar && v2IsVar {
continue
}
if r1[i].Value.Compare(r2[i].Value) != 0 {
return -1
}
}
return 0
}
return 1
}
func (vcKeyScope) Find(ast.Ref) (ast.Value, error) {
return nil, nil
}
func (q vcKeyScope) Hash() int {
var hash int
for _, v := range q.Ref {
if _, ok := v.Value.(ast.Var); ok {
// all vars are equal
hash++
} else {
hash += v.Value.Hash()
}
}
return hash
}
func (q vcKeyScope) IsGround() bool {
return false
}
func (q vcKeyScope) String() string {
buf := make([]string, 0, len(q.Ref))
for _, t := range q.Ref {
if _, ok := t.Value.(ast.Var); ok {
buf = append(buf, "_")
} else {
buf = append(buf, t.String())
}
}
return fmt.Sprintf("<%s>", strings.Join(buf, ","))
}
// reduce removes vars from the tail of the ref.
func (q vcKeyScope) reduce() vcKeyScope {
ref := q.Ref.Copy()
var i int
for i = len(q.Ref) - 1; i >= 0; i-- {
if _, ok := q.Ref[i].Value.(ast.Var); !ok {
break
}
}
ref = ref[:i+1]
return vcKeyScope{ref}
}
func (q vcKeyScope) empty() bool {
return len(q.Ref) == 0
}
func getNestedObject(ref ast.Ref, rootObj *ast.Object, b *bindings, l *ast.Location) (*ast.Object, error) {
current := rootObj
for _, term := range ref {

View File

@@ -58,6 +58,7 @@ type Query struct {
strictObjects bool
printHook print.Hook
tracingOpts tracing.Options
virtualCache VirtualCache
}
// Builtin represents a built-in function that queries can call.
@@ -291,6 +292,13 @@ func (q *Query) WithStrictObjects(yes bool) *Query {
return q
}
// WithVirtualCache sets the VirtualCache to use during evaluation. This is
// optional, and if not set, the default cache is used.
func (q *Query) WithVirtualCache(vc VirtualCache) *Query {
q.virtualCache = vc
return q
}
// PartialRun executes partial evaluation on the query with respect to unknown
// values. Partial evaluation attempts to evaluate as much of the query as
// possible without requiring values for the unknowns set on the query. The
@@ -311,8 +319,17 @@ func (q *Query) PartialRun(ctx context.Context) (partials []ast.Body, support []
if q.metrics == nil {
q.metrics = metrics.New()
}
f := &queryIDFactory{}
b := newBindings(0, q.instr)
var vc VirtualCache
if q.virtualCache != nil {
vc = q.virtualCache
} else {
vc = NewVirtualCache()
}
e := &eval{
ctx: ctx,
metrics: q.metrics,
@@ -340,7 +357,7 @@ func (q *Query) PartialRun(ctx context.Context) (partials []ast.Body, support []
functionMocks: newFunctionMocksStack(),
interQueryBuiltinCache: q.interQueryBuiltinCache,
ndBuiltinCache: q.ndBuiltinCache,
virtualCache: newVirtualCache(),
virtualCache: vc,
comprehensionCache: newComprehensionCache(),
saveSet: newSaveSet(q.unknowns, b, q.instr),
saveStack: newSaveStack(),
@@ -488,7 +505,16 @@ func (q *Query) Iter(ctx context.Context, iter func(QueryResult) error) error {
if q.metrics == nil {
q.metrics = metrics.New()
}
f := &queryIDFactory{}
var vc VirtualCache
if q.virtualCache != nil {
vc = q.virtualCache
} else {
vc = NewVirtualCache()
}
e := &eval{
ctx: ctx,
metrics: q.metrics,
@@ -516,7 +542,7 @@ func (q *Query) Iter(ctx context.Context, iter func(QueryResult) error) error {
functionMocks: newFunctionMocksStack(),
interQueryBuiltinCache: q.interQueryBuiltinCache,
ndBuiltinCache: q.ndBuiltinCache,
virtualCache: newVirtualCache(),
virtualCache: vc,
comprehensionCache: newComprehensionCache(),
genvarprefix: q.genvarprefix,
runtime: q.runtime,

View File

@@ -720,8 +720,10 @@ func (constraints *tokenConstraints) validAudience(aud ast.Value) bool {
// JWT algorithms
type tokenVerifyFunction func(key interface{}, hash crypto.Hash, payload []byte, signature []byte) error
type tokenVerifyAsymmetricFunction func(key interface{}, hash crypto.Hash, digest []byte, signature []byte) error
type (
tokenVerifyFunction func(key interface{}, hash crypto.Hash, payload []byte, signature []byte) error
tokenVerifyAsymmetricFunction func(key interface{}, hash crypto.Hash, digest []byte, signature []byte) error
)
// jwtAlgorithm describes a JWS 'alg' value
type tokenAlgorithm struct {
@@ -912,7 +914,6 @@ func (header *tokenHeader) valid() bool {
}
func commonBuiltinJWTEncodeSign(bctx BuiltinContext, inputHeaders, jwsPayload, jwkSrc string, iter func(*ast.Term) error) error {
keys, err := jwk.ParseString(jwkSrc)
if err != nil {
return err
@@ -946,21 +947,51 @@ func commonBuiltinJWTEncodeSign(bctx BuiltinContext, inputHeaders, jwsPayload, j
if err != nil {
return err
}
return iter(ast.StringTerm(string(jwsCompact)))
return iter(ast.StringTerm(string(jwsCompact)))
}
func builtinJWTEncodeSign(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
inputHeadersAsJSON, err := ast.JSON(operands[0].Value)
if err != nil {
return fmt.Errorf("failed to prepare JWT headers for marshalling: %v", err)
}
inputHeaders := operands[0].String()
jwsPayload := operands[1].String()
jwkSrc := operands[2].String()
return commonBuiltinJWTEncodeSign(bctx, inputHeaders, jwsPayload, jwkSrc, iter)
inputHeadersBs, err := json.Marshal(inputHeadersAsJSON)
if err != nil {
return fmt.Errorf("failed to marshal JWT headers: %v", err)
}
payloadAsJSON, err := ast.JSON(operands[1].Value)
if err != nil {
return fmt.Errorf("failed to prepare JWT payload for marshalling: %v", err)
}
payloadBs, err := json.Marshal(payloadAsJSON)
if err != nil {
return fmt.Errorf("failed to marshal JWT payload: %v", err)
}
signatureAsJSON, err := ast.JSON(operands[2].Value)
if err != nil {
return fmt.Errorf("failed to prepare JWT signature for marshalling: %v", err)
}
signatureBs, err := json.Marshal(signatureAsJSON)
if err != nil {
return fmt.Errorf("failed to marshal JWT signature: %v", err)
}
return commonBuiltinJWTEncodeSign(
bctx,
string(inputHeadersBs),
string(payloadBs),
string(signatureBs),
iter,
)
}
func builtinJWTEncodeSignRaw(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {
jwkSrc, err := builtins.StringOperand(operands[2].Value, 3)
if err != nil {
return err

View File

@@ -94,6 +94,11 @@ type Event struct {
localVirtualCacheSnapshot *ast.ValueMap
}
func (evt *Event) WithInput(input *ast.Term) *Event {
evt.input = input
return evt
}
// HasRule returns true if the Event contains an ast.Rule.
func (evt *Event) HasRule() bool {
_, ok := evt.Node.(*ast.Rule)

View File

@@ -11,7 +11,7 @@ import (
)
// Version is the canonical version of OPA.
var Version = "0.67.1"
var Version = "0.68.0"
// GoVersion is the version of Go this was built with
var GoVersion = runtime.Version()

2
vendor/modules.txt vendored
View File

@@ -1533,7 +1533,7 @@ github.com/onsi/gomega/matchers/support/goraph/edge
github.com/onsi/gomega/matchers/support/goraph/node
github.com/onsi/gomega/matchers/support/goraph/util
github.com/onsi/gomega/types
# github.com/open-policy-agent/opa v0.67.1
# github.com/open-policy-agent/opa v0.68.0
## explicit; go 1.21
github.com/open-policy-agent/opa/ast
github.com/open-policy-agent/opa/ast/internal/scanner