From f50bafeb88c5f4eddaf5f4aac7d84f9ee81963ac Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 3 Sep 2026 07:02:08 +0000 Subject: [PATCH 1/2] build(deps): bump github.com/open-policy-agent/opa from 1.19.0 to 1.20.1 Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 1.19.0 to 1.20.1. - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v1.19.0...v1.20.1) --- updated-dependencies: - dependency-name: github.com/open-policy-agent/opa dependency-version: 1.20.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 16 +- .../opa/capabilities/v1.19.1.json | 5026 ++++++++++++++++ .../opa/capabilities/v1.20.0.json | 5028 +++++++++++++++++ .../opa/capabilities/v1.20.1.json | 5028 +++++++++++++++++ .../opa/internal/edittree/edittree.go | 13 +- .../opa/internal/file/archive/tarball.go | 9 +- .../opa/internal/gojsonschema/errors.go | 1 - .../opa/internal/gojsonschema/jsonLoader.go | 110 +- .../opa/internal/gojsonschema/schema.go | 28 +- .../opa/internal/gojsonschema/schemaLoader.go | 53 +- .../opa/internal/gojsonschema/utils.go | 1 - .../opa/internal/gojsonschema/validation.go | 5 +- .../opa/internal/lcss/qsufsort.go | 13 +- .../opa/internal/leb128/leb128.go | 10 +- .../opa/internal/methodlesstemplate/funcs.go | 8 - .../internal/fmtsort/sort.go | 6 +- .../internal/methodlesstemplate/template.go | 4 +- .../opa/internal/planner/planner.go | 86 +- .../opa/internal/planner/rules.go | 11 +- .../opa/internal/planner/varstack.go | 6 +- .../opa/internal/providers/aws/crypto/ecc.go | 54 +- .../opa/internal/providers/aws/signing_v4.go | 26 +- .../opa/internal/providers/aws/signing_v4a.go | 22 +- .../opa/internal/providers/aws/util.go | 2 +- .../opa/internal/providers/aws/v4/host.go | 37 +- .../opa/internal/semver/semver.go | 6 +- .../opa/internal/uuid/uuid.go | 27 +- .../opa/internal/version/version.go | 4 +- .../opa/internal/wasm/encoding/reader.go | 5 +- .../opa/capabilities/capabilities_nowasm.go | 1 - .../opa/v1/ast/annotations.go | 357 +- .../open-policy-agent/opa/v1/ast/builtins.go | 26 +- .../opa/v1/ast/capabilities.go | 35 +- .../open-policy-agent/opa/v1/ast/check.go | 178 +- .../open-policy-agent/opa/v1/ast/compare.go | 92 +- .../open-policy-agent/opa/v1/ast/compile.go | 511 +- .../open-policy-agent/opa/v1/ast/env.go | 19 +- .../open-policy-agent/opa/v1/ast/index.go | 334 +- .../opa/v1/ast/index_debug.go | 29 +- .../open-policy-agent/opa/v1/ast/interning.go | 41 +- .../opa/v1/ast/location/location.go | 28 +- .../open-policy-agent/opa/v1/ast/mermaid.go | 4 +- .../open-policy-agent/opa/v1/ast/object.go | 166 + .../open-policy-agent/opa/v1/ast/parser.go | 569 +- .../opa/v1/ast/parser_ext.go | 55 +- .../opa/v1/ast/performance.go | 35 +- .../open-policy-agent/opa/v1/ast/policy.go | 122 +- .../opa/v1/ast/policy_appenders.go | 98 +- .../opa/v1/ast/policy_jsonv2.go | 86 +- .../open-policy-agent/opa/v1/ast/slices.go | 15 - .../opa/v1/ast/string_length.go | 187 +- .../open-policy-agent/opa/v1/ast/syncpools.go | 8 +- .../open-policy-agent/opa/v1/ast/term.go | 330 +- .../opa/v1/ast/term_appenders.go | 36 +- .../open-policy-agent/opa/v1/ast/term_json.go | 4 +- .../opa/v1/ast/term_jsonv2.go | 40 +- .../open-policy-agent/opa/v1/ast/transform.go | 10 +- .../opa/v1/ast/treenode_dump.go | 16 +- .../open-policy-agent/opa/v1/ast/varset.go | 8 +- .../opa/v1/ast/version_index.json | 10 + .../open-policy-agent/opa/v1/bundle/bundle.go | 21 +- .../open-policy-agent/opa/v1/bundle/file.go | 26 +- .../open-policy-agent/opa/v1/bundle/proto.go | 39 +- .../open-policy-agent/opa/v1/bundle/store.go | 48 +- .../opa/v1/bundle/v1pb/manifest.pb.go | 2 +- .../open-policy-agent/opa/v1/format/format.go | 666 ++- .../open-policy-agent/opa/v1/ir/ir.go | 55 +- .../open-policy-agent/opa/v1/ir/marshal.go | 8 +- .../open-policy-agent/opa/v1/ir/plan.proto | 39 +- .../opa/v1/ir/plan.schema.json | 329 ++ .../open-policy-agent/opa/v1/ir/proto.go | 90 +- .../opa/v1/ir/v1pb/plan.pb.go | 47 +- .../open-policy-agent/opa/v1/loader/loader.go | 28 + .../opa/v1/logging/logging.go | 10 +- .../opa/v1/metrics/metrics.go | 8 +- .../open-policy-agent/opa/v1/rego/rego.go | 119 +- .../opa/v1/storage/errors.go | 6 +- .../opa/v1/storage/inmem/inmem.go | 14 +- .../open-policy-agent/opa/v1/storage/path.go | 17 +- .../opa/v1/topdown/aggregates.go | 99 +- .../opa/v1/topdown/arithmetic.go | 5 +- .../opa/v1/topdown/builtins.go | 18 +- .../open-policy-agent/opa/v1/topdown/cache.go | 10 +- .../open-policy-agent/opa/v1/topdown/cidr.go | 46 +- .../opa/v1/topdown/comparison.go | 44 +- .../copypropagation/copypropagation.go | 32 +- .../opa/v1/topdown/errors.go | 16 +- .../open-policy-agent/opa/v1/topdown/eval.go | 255 +- .../open-policy-agent/opa/v1/topdown/http.go | 819 ++- .../open-policy-agent/opa/v1/topdown/json.go | 4 +- .../opa/v1/topdown/jsonschema.go | 24 +- .../open-policy-agent/opa/v1/topdown/net.go | 2 +- .../opa/v1/topdown/parse_bytes.go | 6 +- .../opa/v1/topdown/providers.go | 6 +- .../open-policy-agent/opa/v1/topdown/query.go | 24 +- .../opa/v1/topdown/reachable.go | 8 +- .../opa/v1/topdown/regex_template.go | 12 +- .../open-policy-agent/opa/v1/topdown/save.go | 131 +- .../open-policy-agent/opa/v1/topdown/sets.go | 33 +- .../opa/v1/topdown/strings.go | 40 +- .../open-policy-agent/opa/v1/topdown/test.go | 3 +- .../open-policy-agent/opa/v1/topdown/time.go | 31 +- .../open-policy-agent/opa/v1/topdown/trace.go | 178 +- .../open-policy-agent/opa/v1/topdown/uuid.go | 11 +- .../open-policy-agent/opa/v1/types/types.go | 103 +- .../open-policy-agent/opa/v1/util/compare.go | 20 + .../opa/v1/util/constraints.go | 19 + .../open-policy-agent/opa/v1/util/maps.go | 8 +- .../opa/v1/util/performance.go | 75 +- .../open-policy-agent/opa/v1/util/slices.go | 74 + .../opa/v1/version/version.go | 2 +- vendor/modules.txt | 4 +- 113 files changed, 19884 insertions(+), 2947 deletions(-) create mode 100644 vendor/github.com/open-policy-agent/opa/capabilities/v1.19.1.json create mode 100644 vendor/github.com/open-policy-agent/opa/capabilities/v1.20.0.json create mode 100644 vendor/github.com/open-policy-agent/opa/capabilities/v1.20.1.json create mode 100644 vendor/github.com/open-policy-agent/opa/v1/ast/object.go delete mode 100644 vendor/github.com/open-policy-agent/opa/v1/ast/slices.go create mode 100644 vendor/github.com/open-policy-agent/opa/v1/util/constraints.go diff --git a/go.mod b/go.mod index 5a2ca81067..1ce609acd7 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( github.com/linuxkit/virtsock v0.0.0-20241009230534-cb6a20cc0422 github.com/mattn/go-shellwords v1.0.13 github.com/moby/sys/user v0.4.0 - github.com/open-policy-agent/opa v1.19.0 + github.com/open-policy-agent/opa v1.20.1 github.com/opencontainers/cgroups v0.1.0 github.com/opencontainers/runc v1.4.3 github.com/opencontainers/runtime-spec v1.3.0 diff --git a/go.sum b/go.sum index a437508770..4a5a94f822 100644 --- a/go.sum +++ b/go.sum @@ -481,8 +481,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= -github.com/dgraph-io/badger/v4 v4.9.4 h1:bcw+waCpzRZ2nmcSPbnPvDVhiEsn98TKmvnAhK7r7LM= -github.com/dgraph-io/badger/v4 v4.9.4/go.mod h1:nJjaJTUOSsQEBhsq209FmwCvMJzEA3e74RjZw6V2pQI= +github.com/dgraph-io/badger/v4 v4.9.5 h1:zT46OMrF3ntqsfI3ynKp7hUkQrGlcK2CX5psQmH0iW0= +github.com/dgraph-io/badger/v4 v4.9.5/go.mod h1:Xa9dAupjbwAacupWFCpa6YEn9E1PjBXkfZYr2I/8aWg= github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM= github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= @@ -841,8 +841,8 @@ github.com/mrunalp/fileutils v0.5.1 h1:F+S7ZlNKnrwHfSwdlgNSkKo67ReVf8o9fel6C3dkm github.com/mrunalp/fileutils v0.5.1/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/open-policy-agent/opa v1.19.0 h1:+j2OCsjMezZEML2T1lI9giJdGJS/PL1XFKgkHPGIhpo= -github.com/open-policy-agent/opa v1.19.0/go.mod h1:pb6Y6klyf7X7X8uXNDflruA9dQC2gMqWROXI5w/kvv0= +github.com/open-policy-agent/opa v1.20.1 h1:wSC3zjHbRyt7X3daV/DsjnhDywzB3l0m0gWhgX1W2vQ= +github.com/open-policy-agent/opa v1.20.1/go.mod h1:pxxSP1noAirD8UJ7PgAjoRw39IE0Bk/JRFkUP3+51lU= github.com/opencontainers/cgroups v0.1.0 h1:6W05KiDvgj1yi/6D13SJCvYx8TAESz67szvHPxhfcrs= github.com/opencontainers/cgroups v0.1.0/go.mod h1:hPBRvnBhLZueEN0eJyozMeM3HeFGYlZW9KnO//px6G4= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -870,8 +870,8 @@ github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.24.0 h1:5XStIklKuAtJSNpdD3s8XJj/Yv78IQmE1kbNk87JrAI= -github.com/prometheus/client_golang v1.24.0/go.mod h1:QcsNdotprC2nS4BTM2ucbcqxd2CeXTEa9jW7zHO9iDE= +github.com/prometheus/client_golang v1.24.1 h1:JnJkREXzWxUdCuPFpIWZiPispT9xVV59uiuyR2bPlnU= +github.com/prometheus/client_golang v1.24.1/go.mod h1:F+oSRECHg4sse5ucfYpYDeIv/hu68Zo0uoHKetWnzcE= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= @@ -881,8 +881,8 @@ github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOA github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.70.0 h1:bcpru3tWPVnxGnETLgOV5jbp/JRXgYEyv65CuBLAMMI= -github.com/prometheus/common v0.70.0/go.mod h1:S/SFasQmgGiYH6C81LKCtYa8QACgthGg5zxL2udV7SY= +github.com/prometheus/common v0.70.1 h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi/PY= +github.com/prometheus/common v0.70.1/go.mod h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc= github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI= github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY= github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg= diff --git a/vendor/github.com/open-policy-agent/opa/capabilities/v1.19.1.json b/vendor/github.com/open-policy-agent/opa/capabilities/v1.19.1.json new file mode 100644 index 0000000000..1b02dd4ace --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/capabilities/v1.19.1.json @@ -0,0 +1,5026 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "and", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "infix": "\u0026" + }, + { + "name": "any", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "array.concat", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.flatten", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.reverse", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.slice", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "assign", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": ":=" + }, + { + "name": "base64.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode_no_pad", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "bits.and", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.lsh", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.negate", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.or", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.rsh", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.xor", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "cast_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "null" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "ceil", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "contains", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "count", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.equal", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.md5", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha1", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.parse_private_keys", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.sha1", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.sha256", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_and_verify_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_and_verify_certificates_with_options", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_certificate_request", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_keypair", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_rsa_private_key", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "div", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "/" + }, + { + "name": "endswith", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "eq", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "=" + }, + { + "name": "equal", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "==" + }, + { + "name": "floor", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "type": "null" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "type": "any" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "glob.quote_meta", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "graph.reachable", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "graph.reachable_paths", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "graphql.is_valid", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "graphql.parse", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_and_verify", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_query", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_schema", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "graphql.schema_is_valid", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "gt", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003e" + }, + { + "name": "gte", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003e=" + }, + { + "name": "hex.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "hex.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "http.send", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "indexof_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "internal.member_2", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "in" + }, + { + "name": "internal.member_3", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "in" + }, + { + "name": "internal.print", + "decl": { + "args": [ + { + "dynamic": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "internal.template_string", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "internal.test_case", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "intersection", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode_verify", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.verify_eddsa", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "json.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "json.marshal_with_options", + "decl": { + "args": [ + { + "type": "any" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "static": [ + { + "key": "indent", + "value": { + "type": "string" + } + }, + { + "key": "prefix", + "value": { + "type": "string" + } + }, + { + "key": "pretty", + "value": { + "type": "boolean" + } + } + ], + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "json.match_schema", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "static": [ + { + "key": "desc", + "value": { + "type": "string" + } + }, + { + "key": "error", + "value": { + "type": "string" + } + }, + { + "key": "field", + "value": { + "type": "string" + } + }, + { + "key": "type", + "value": { + "type": "string" + } + } + ], + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "json.patch", + "decl": { + "args": [ + { + "type": "any" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "static": [ + { + "key": "op", + "value": { + "type": "string" + } + }, + { + "key": "path", + "value": { + "type": "any" + } + } + ], + "type": "object" + }, + "type": "array" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "json.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.verify_schema", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "of": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "lower", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "lt", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003c" + }, + { + "name": "lte", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003c=" + }, + { + "name": "max", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "minus", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + "type": "function" + }, + "infix": "-" + }, + { + "name": "mul", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "*" + }, + { + "name": "neq", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "!=" + }, + { + "name": "net.cidr_contains", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_contains_matches", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "static": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_expand", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_intersects", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_merge", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "net.lookup_ip_addr", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "numbers.range_step", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.get", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "object.keys", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "object.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.subset", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.union", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.union_n", + "decl": { + "args": [ + { + "dynamic": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "or", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "infix": "|" + }, + { + "name": "plus", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "+" + }, + { + "name": "print", + "decl": { + "type": "function", + "variadic": { + "type": "any" + } + } + }, + { + "name": "product", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + { + "of": { + "type": "number" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "providers.aws.sign_req", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "re_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "regex.find_all_string_submatch_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.find_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.globs_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.replace", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "regex.split", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.template_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "rego.metadata.chain", + "decl": { + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "rego.metadata.rule", + "decl": { + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "rego.parse_module", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "rem", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "%" + }, + { + "name": "replace", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "round", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "semver.compare", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "semver.is_valid", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "set_diff", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "sort", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "split", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "sprintf", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "startswith", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.any_prefix_match", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.any_suffix_match", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.split_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "substring", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "sum", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + { + "of": { + "type": "number" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.add_date", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.clock", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.date", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.diff", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.format", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "time.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "time.parse_duration_ns", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.parse_ns", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.parse_rfc3339_ns", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.weekday", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "to_number", + "decl": { + "args": [ + { + "of": [ + { + "type": "null" + }, + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "trace", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "trim", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_left", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_prefix", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_right", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_space", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_suffix", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "type_name", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "union", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "units.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "units.parse_bytes", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "upper", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "uri.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "uri.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "urlquery.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "urlquery.decode_object", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "dynamic": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "urlquery.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "urlquery.encode_object", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "uuid.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "uuid.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ], + "future_keywords": [ + "not" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "keywords_in_refs", + "rego_v1", + "template_strings" + ] +} diff --git a/vendor/github.com/open-policy-agent/opa/capabilities/v1.20.0.json b/vendor/github.com/open-policy-agent/opa/capabilities/v1.20.0.json new file mode 100644 index 0000000000..ccecfa24b1 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/capabilities/v1.20.0.json @@ -0,0 +1,5028 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "and", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "infix": "\u0026" + }, + { + "name": "any", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "array.concat", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.flatten", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.reverse", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.slice", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "assign", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": ":=" + }, + { + "name": "base64.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode_no_pad", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "bits.and", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.lsh", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.negate", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.or", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.rsh", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.xor", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "cast_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "null" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "ceil", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "contains", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "count", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.equal", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.md5", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha1", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.parse_private_keys", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.sha1", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.sha256", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_and_verify_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_and_verify_certificates_with_options", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_certificate_request", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_keypair", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_rsa_private_key", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "div", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "/" + }, + { + "name": "endswith", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "eq", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "=" + }, + { + "name": "equal", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "==" + }, + { + "name": "floor", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "type": "null" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "type": "any" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "glob.quote_meta", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "graph.reachable", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "graph.reachable_paths", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "graphql.is_valid", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "graphql.parse", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_and_verify", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_query", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_schema", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "graphql.schema_is_valid", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "gt", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003e" + }, + { + "name": "gte", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003e=" + }, + { + "name": "hex.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "hex.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "http.send", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "indexof_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "internal.member_2", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "in" + }, + { + "name": "internal.member_3", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "in" + }, + { + "name": "internal.print", + "decl": { + "args": [ + { + "dynamic": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "internal.template_string", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "internal.test_case", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "intersection", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode_verify", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.verify_eddsa", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "json.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "json.marshal_with_options", + "decl": { + "args": [ + { + "type": "any" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "static": [ + { + "key": "indent", + "value": { + "type": "string" + } + }, + { + "key": "prefix", + "value": { + "type": "string" + } + }, + { + "key": "pretty", + "value": { + "type": "boolean" + } + } + ], + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "json.match_schema", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "static": [ + { + "key": "desc", + "value": { + "type": "string" + } + }, + { + "key": "error", + "value": { + "type": "string" + } + }, + { + "key": "field", + "value": { + "type": "string" + } + }, + { + "key": "type", + "value": { + "type": "string" + } + } + ], + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "json.patch", + "decl": { + "args": [ + { + "type": "any" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "static": [ + { + "key": "op", + "value": { + "type": "string" + } + }, + { + "key": "path", + "value": { + "type": "any" + } + } + ], + "type": "object" + }, + "type": "array" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "json.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.verify_schema", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "of": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "lower", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "lt", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003c" + }, + { + "name": "lte", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003c=" + }, + { + "name": "max", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "minus", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + "type": "function" + }, + "infix": "-" + }, + { + "name": "mul", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "*" + }, + { + "name": "neq", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "!=" + }, + { + "name": "net.cidr_contains", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_contains_matches", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "static": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_expand", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_intersects", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_merge", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "net.lookup_ip_addr", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "numbers.range_step", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.get", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "object.keys", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "object.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.subset", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.union", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.union_n", + "decl": { + "args": [ + { + "dynamic": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "or", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "infix": "|" + }, + { + "name": "plus", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "+" + }, + { + "name": "print", + "decl": { + "type": "function", + "variadic": { + "type": "any" + } + } + }, + { + "name": "product", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + { + "of": { + "type": "number" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "providers.aws.sign_req", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "re_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "regex.find_all_string_submatch_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.find_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.globs_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.replace", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "regex.split", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.template_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "rego.metadata.chain", + "decl": { + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "rego.metadata.rule", + "decl": { + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "rego.parse_module", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "rem", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "%" + }, + { + "name": "replace", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "round", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "semver.compare", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "semver.is_valid", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "set_diff", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "sort", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "split", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "sprintf", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "startswith", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.any_prefix_match", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.any_suffix_match", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.split_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "substring", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "sum", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + { + "of": { + "type": "number" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.add_date", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.clock", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.date", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.diff", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.format", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "time.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "time.parse_duration_ns", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.parse_ns", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.parse_rfc3339_ns", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.weekday", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "to_number", + "decl": { + "args": [ + { + "of": [ + { + "type": "null" + }, + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "trace", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "trim", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_left", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_prefix", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_right", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_space", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_suffix", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "type_name", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "union", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "units.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "units.parse_bytes", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "upper", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "uri.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "uri.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "urlquery.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "urlquery.decode_object", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "dynamic": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "urlquery.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "urlquery.encode_object", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "uuid.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "uuid.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ], + "future_keywords": [ + "and", + "not", + "or" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "keywords_in_refs", + "rego_v1", + "template_strings" + ] +} diff --git a/vendor/github.com/open-policy-agent/opa/capabilities/v1.20.1.json b/vendor/github.com/open-policy-agent/opa/capabilities/v1.20.1.json new file mode 100644 index 0000000000..ccecfa24b1 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/capabilities/v1.20.1.json @@ -0,0 +1,5028 @@ +{ + "builtins": [ + { + "name": "abs", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "all", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "and", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "infix": "\u0026" + }, + { + "name": "any", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "array.concat", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.flatten", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.reverse", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "array.slice", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "assign", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": ":=" + }, + { + "name": "base64.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "base64url.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "base64url.encode_no_pad", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "bits.and", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.lsh", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.negate", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.or", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.rsh", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "bits.xor", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "cast_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "null" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "cast_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "ceil", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "concat", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "contains", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "count", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.equal", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.md5", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha1", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.hmac.sha512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.md5", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.parse_private_keys", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.sha1", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.sha256", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_and_verify_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_and_verify_certificates_with_options", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_certificate_request", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_certificates", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_keypair", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "crypto.x509.parse_rsa_private_key", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "div", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "/" + }, + { + "name": "endswith", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "eq", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "=" + }, + { + "name": "equal", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "==" + }, + { + "name": "floor", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "format_int", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "glob.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "of": [ + { + "type": "null" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "type": "any" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "glob.quote_meta", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "graph.reachable", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "graph.reachable_paths", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "graphql.is_valid", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "graphql.parse", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_and_verify", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_query", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "graphql.parse_schema", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "graphql.schema_is_valid", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "gt", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003e" + }, + { + "name": "gte", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003e=" + }, + { + "name": "hex.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "hex.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "http.send", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "indexof", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "indexof_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "internal.member_2", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "in" + }, + { + "name": "internal.member_3", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "in" + }, + { + "name": "internal.print", + "decl": { + "args": [ + { + "dynamic": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "internal.template_string", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "internal.test_case", + "decl": { + "args": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "function" + } + }, + { + "name": "intersection", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "static": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "io.jwt.decode_verify", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "array" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.encode_sign_raw", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "io.jwt.verify_eddsa", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_es512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_hs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_ps512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs256", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs384", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "io.jwt.verify_rs512", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_array", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_number", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "is_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "json.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "json.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "json.marshal_with_options", + "decl": { + "args": [ + { + "type": "any" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "static": [ + { + "key": "indent", + "value": { + "type": "string" + } + }, + { + "key": "prefix", + "value": { + "type": "string" + } + }, + { + "key": "pretty", + "value": { + "type": "boolean" + } + } + ], + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "json.match_schema", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "dynamic": { + "static": [ + { + "key": "desc", + "value": { + "type": "string" + } + }, + { + "key": "error", + "value": { + "type": "string" + } + }, + { + "key": "field", + "value": { + "type": "string" + } + }, + { + "key": "type", + "value": { + "type": "string" + } + } + ], + "type": "object" + }, + "type": "array" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "json.patch", + "decl": { + "args": [ + { + "type": "any" + }, + { + "dynamic": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "static": [ + { + "key": "op", + "value": { + "type": "string" + } + }, + { + "key": "path", + "value": { + "type": "any" + } + } + ], + "type": "object" + }, + "type": "array" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "json.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "json.verify_schema", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "boolean" + }, + { + "of": [ + { + "type": "null" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "lower", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "lt", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003c" + }, + { + "name": "lte", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "\u003c=" + }, + { + "name": "max", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "min", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "minus", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": [ + { + "type": "number" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + "type": "function" + }, + "infix": "-" + }, + { + "name": "mul", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "*" + }, + { + "name": "neq", + "decl": { + "args": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "infix": "!=" + }, + { + "name": "net.cidr_contains", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_contains_matches", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "static": [ + { + "type": "any" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_expand", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_intersects", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "net.cidr_merge", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "of": [ + { + "type": "string" + } + ], + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "net.cidr_overlap", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "net.lookup_ip_addr", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "of": { + "type": "string" + }, + "type": "set" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "numbers.range", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "numbers.range_step", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "object.filter", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.get", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "any" + }, + { + "type": "any" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "object.keys", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "object.remove", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.subset", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "object.union", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "object.union_n", + "decl": { + "args": [ + { + "dynamic": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "opa.runtime", + "decl": { + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "or", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "infix": "|" + }, + { + "name": "plus", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "+" + }, + { + "name": "print", + "decl": { + "type": "function", + "variadic": { + "type": "any" + } + } + }, + { + "name": "product", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + { + "of": { + "type": "number" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "providers.aws.sign_req", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "rand.intn", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "re_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "regex.find_all_string_submatch_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.find_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.globs_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "regex.replace", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "regex.split", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "regex.template_match", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "rego.metadata.chain", + "decl": { + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "rego.metadata.rule", + "decl": { + "result": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "rego.parse_module", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "rem", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + }, + "infix": "%" + }, + { + "name": "replace", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "round", + "decl": { + "args": [ + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "semver.compare", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "semver.is_valid", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "set_diff", + "decl": { + "args": [ + { + "of": { + "type": "any" + }, + "type": "set" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + }, + "deprecated": true + }, + { + "name": "sort", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "of": { + "type": "any" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "split", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "sprintf", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "any" + }, + "type": "array" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "startswith", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.any_prefix_match", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.any_suffix_match", + "decl": { + "args": [ + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "strings.count", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "strings.render_template", + "decl": { + "args": [ + { + "type": "string" + }, + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.replace_n", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.reverse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "strings.split_n", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + }, + { + "type": "number" + } + ], + "result": { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + "type": "function" + } + }, + { + "name": "substring", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "sum", + "decl": { + "args": [ + { + "of": [ + { + "dynamic": { + "type": "number" + }, + "type": "array" + }, + { + "of": { + "type": "number" + }, + "type": "set" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.add_date", + "decl": { + "args": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.clock", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.date", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.diff", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + }, + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "static": [ + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + }, + { + "type": "number" + } + ], + "type": "array" + }, + "type": "function" + } + }, + { + "name": "time.format", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "time.now_ns", + "decl": { + "result": { + "type": "number" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "time.parse_duration_ns", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.parse_ns", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.parse_rfc3339_ns", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "time.weekday", + "decl": { + "args": [ + { + "of": [ + { + "type": "number" + }, + { + "static": [ + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "array" + } + ], + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "to_number", + "decl": { + "args": [ + { + "of": [ + { + "type": "null" + }, + { + "type": "boolean" + }, + { + "type": "number" + }, + { + "type": "string" + } + ], + "type": "any" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "trace", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "trim", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_left", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_prefix", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_right", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_space", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "trim_suffix", + "decl": { + "args": [ + { + "type": "string" + }, + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "type_name", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "union", + "decl": { + "args": [ + { + "of": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "set" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "units.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "units.parse_bytes", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "number" + }, + "type": "function" + } + }, + { + "name": "upper", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "uri.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "uri.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "urlquery.decode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "urlquery.decode_object", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "dynamic": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "urlquery.encode", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "urlquery.encode_object", + "decl": { + "args": [ + { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "of": [ + { + "type": "string" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + }, + { + "of": { + "type": "string" + }, + "type": "set" + } + ], + "type": "any" + } + }, + "type": "object" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "uuid.parse", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "dynamic": { + "key": { + "type": "string" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "uuid.rfc4122", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "string" + }, + "type": "function" + }, + "nondeterministic": true + }, + { + "name": "walk", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "static": [ + { + "dynamic": { + "type": "any" + }, + "type": "array" + }, + { + "type": "any" + } + ], + "type": "array" + }, + "type": "function" + }, + "relation": true + }, + { + "name": "yaml.is_valid", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "yaml.marshal", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "name": "yaml.unmarshal", + "decl": { + "args": [ + { + "type": "string" + } + ], + "result": { + "type": "any" + }, + "type": "function" + } + } + ], + "future_keywords": [ + "and", + "not", + "or" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "keywords_in_refs", + "rego_v1", + "template_strings" + ] +} diff --git a/vendor/github.com/open-policy-agent/opa/internal/edittree/edittree.go b/vendor/github.com/open-policy-agent/opa/internal/edittree/edittree.go index b710925199..39821f2e59 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/edittree/edittree.go +++ b/vendor/github.com/open-policy-agent/opa/internal/edittree/edittree.go @@ -168,6 +168,7 @@ package edittree import ( "errors" "fmt" + "slices" "strings" "github.com/open-policy-agent/opa/internal/edittree/bitvector" @@ -435,16 +436,16 @@ func (e *EditTree) unsafeInsertArray(idx int, value *ast.Term) *EditTree { } } // Do rewrites in reverse order to make room for the newly-inserted element. - for i := len(rewritesScalars) - 1; i >= 0; i-- { - originalIdx := rewritesScalars[i] - rewriteIdx := rewritesScalars[i] + 1 + for _, originalIdx := range slices.Backward(rewritesScalars) { + + rewriteIdx := originalIdx + 1 v := e.childScalarValues[originalIdx] e.deleteChildValue(originalIdx) e.setChildScalarValue(rewriteIdx, v) } - for i := len(rewritesComposites) - 1; i >= 0; i-- { - originalIdx := rewritesComposites[i] - rewriteIdx := rewritesComposites[i] + 1 + for _, originalIdx := range slices.Backward(rewritesComposites) { + + rewriteIdx := originalIdx + 1 v := e.childCompositeValues[originalIdx] e.deleteChildValue(originalIdx) e.setChildCompositeValue(rewriteIdx, v) diff --git a/vendor/github.com/open-policy-agent/opa/internal/file/archive/tarball.go b/vendor/github.com/open-policy-agent/opa/internal/file/archive/tarball.go index 93396aa96f..28189f7f99 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/file/archive/tarball.go +++ b/vendor/github.com/open-policy-agent/opa/internal/file/archive/tarball.go @@ -7,7 +7,8 @@ import ( "encoding/json" "errors" "io" - "strings" + + "github.com/open-policy-agent/opa/v1/util" ) type TarGzWriter struct { @@ -62,11 +63,7 @@ func MustWriteTarGz(files [][2]string) *bytes.Buffer { defer tgw.Close() for _, file := range files { - if !strings.HasPrefix(file[0], "/") { - file[0] = "/" + file[0] - } - - if err := tgw.WriteFile(file[0], []byte(file[1])); err != nil { + if err := tgw.WriteFile(util.WithPrefix(file[0], "/"), []byte(file[1])); err != nil { panic(err) } } diff --git a/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/errors.go b/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/errors.go index 0168f59727..3ffde1e2a3 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/errors.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/errors.go @@ -1,4 +1,3 @@ -// nolint: goconst // String duplication will be handled later by using errors.Is. package gojsonschema import ( diff --git a/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/jsonLoader.go b/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/jsonLoader.go index 73f25e3b7f..569faf6060 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/jsonLoader.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/jsonLoader.go @@ -28,6 +28,7 @@ package gojsonschema import ( "bytes" + "context" "encoding/json" "errors" "fmt" @@ -38,39 +39,40 @@ import ( "path/filepath" "runtime" "strings" - "sync" "github.com/xeipuuv/gojsonreference" ) -// NOTE(sr): We need to control from which hosts remote references are -// allowed to be resolved via HTTP requests. It's quite cumbersome to -// add extra parameters to all calls and interfaces involved, so we're -// using a global variable instead: -var allowNet map[string]struct{} -var netMut sync.RWMutex +// maxRemoteRefRedirects bounds the redirect chain a single remote reference +// fetch may follow. net/http applies its own limit only when CheckRedirect is +// nil, so policing the allowlist there means reimposing it here; the value +// matches the stdlib default. +const maxRemoteRefRedirects = 10 -func SetAllowNet(hosts []string) { - netMut.Lock() - defer netMut.Unlock() +// remoteRefLimits bounds the outbound requests a loader may make while +// resolving remote references. The zero value is unrestricted. +type remoteRefLimits struct { + // A nil set permits any host; an empty set permits none. + allowNet map[string]struct{} + + // LoadJSON takes no arguments, so the context rides on the loader instead. + // Loaders are built per Compile call, so its scope is that one compilation. + // A nil ctx means context.Background(). + ctx context.Context +} + +// newAllowNetSet turns a list of permitted hosts into a set. A nil list +// yields a nil set, which permits every host; a non-nil empty list yields +// an empty set, which permits none. +func newAllowNetSet(hosts []string) map[string]struct{} { if hosts == nil { - allowNet = nil // resetting the global - return + return nil } - allowNet = make(map[string]struct{}, len(hosts)) + allowNet := make(map[string]struct{}, len(hosts)) for _, host := range hosts { allowNet[host] = struct{}{} } -} - -func isAllowed(ref *url.URL) bool { - netMut.RLock() - defer netMut.RUnlock() - if allowNet == nil { - return true - } - _, ok := allowNet[ref.Hostname()] - return ok + return allowNet } var osFS = osFileSystem(os.Open) @@ -87,15 +89,20 @@ type JSONLoader interface { type JSONLoaderFactory interface { // New creates a new JSON loader for the given source New(source string) JSONLoader + // withRemoteRefLimits returns a copy of the factory whose loaders resolve + // remote references subject to the given limits. + withRemoteRefLimits(limits remoteRefLimits) JSONLoaderFactory } // DefaultJSONLoaderFactory is the default JSON loader factory type DefaultJSONLoaderFactory struct { + limits remoteRefLimits } // FileSystemJSONLoaderFactory is a JSON loader factory that uses http.FileSystem type FileSystemJSONLoaderFactory struct { - fs http.FileSystem + fs http.FileSystem + limits remoteRefLimits } // New creates a new JSON loader for the given source @@ -103,6 +110,7 @@ func (d DefaultJSONLoaderFactory) New(source string) JSONLoader { return &jsonReferenceLoader{ fs: osFS, source: source, + limits: d.limits, } } @@ -111,9 +119,20 @@ func (f FileSystemJSONLoaderFactory) New(source string) JSONLoader { return &jsonReferenceLoader{ fs: f.fs, source: source, + limits: f.limits, } } +func (d DefaultJSONLoaderFactory) withRemoteRefLimits(limits remoteRefLimits) JSONLoaderFactory { + d.limits = limits + return d +} + +func (f FileSystemJSONLoaderFactory) withRemoteRefLimits(limits remoteRefLimits) JSONLoaderFactory { + f.limits = limits + return f +} + // osFileSystem is a functional wrapper for os.Open that implements http.FileSystem. type osFileSystem func(string) (*os.File, error) @@ -128,6 +147,22 @@ func (o osFileSystem) Open(name string) (http.File, error) { type jsonReferenceLoader struct { fs http.FileSystem source string + limits remoteRefLimits +} + +func (l *jsonReferenceLoader) isAllowed(ref *url.URL) bool { + if l.limits.allowNet == nil { + return true + } + _, ok := l.limits.allowNet[ref.Hostname()] + return ok +} + +func (l *jsonReferenceLoader) context() context.Context { + if l.limits.ctx == nil { + return context.Background() + } + return l.limits.ctx } func (l *jsonReferenceLoader) JSONSource() any { @@ -140,7 +175,8 @@ func (l *jsonReferenceLoader) JSONReference() (gojsonreference.JsonReference, er func (l *jsonReferenceLoader) LoaderFactory() JSONLoaderFactory { return &FileSystemJSONLoaderFactory{ - fs: l.fs, + fs: l.fs, + limits: l.limits, } } @@ -200,7 +236,7 @@ func (l *jsonReferenceLoader) LoadJSON() (any, error) { return decodeJSONUsingNumber(strings.NewReader(metaSchema)) } - if isAllowed(refToURL.GetUrl()) { + if l.isAllowed(refToURL.GetUrl()) { return l.loadFromHTTP(refToURL.String()) } @@ -209,10 +245,30 @@ func (l *jsonReferenceLoader) LoadJSON() (any, error) { func (l *jsonReferenceLoader) loadFromHTTP(address string) (any, error) { - resp, err := http.Get(address) + client := &http.Client{ + CheckRedirect: func(req *http.Request, via []*http.Request) error { + if len(via) >= maxRemoteRefRedirects { + return fmt.Errorf("stopped after %d redirects", maxRemoteRefRedirects) + } + // Checking every hop, not just the first, stops a permitted host + // from bouncing the request onward to one that isn't allowed. + if !l.isAllowed(req.URL) { + return fmt.Errorf("remote reference loading disabled: %s", req.URL.String()) + } + return nil + }, + } + + req, err := http.NewRequestWithContext(l.context(), http.MethodGet, address, nil) + if err != nil { + return nil, err + } + + resp, err := client.Do(req) if err != nil { return nil, err } + defer resp.Body.Close() // must return HTTP Status 200 OK if resp.StatusCode != http.StatusOK { diff --git a/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/schema.go b/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/schema.go index 89f4f52177..4e0d66e424 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/schema.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/schema.go @@ -676,18 +676,24 @@ func (d *Schema) parseSchema(documentNode any, currentSchema *SubSchema) error { if err != nil { return err } - for _, v := range enum { - is, err := marshalWithoutNumber(v) - if err != nil { - return err - } - if isStringInSlice(currentSchema.enum, *is) { - return errors.New(formatErrorDescription( - Locale.KeyItemsMustBeUnique(), - ErrorDetails{"key": KeyEnum}, - )) + // Distinguish a present empty enum from a missing enum keyword. + // JSON Schema: enum validation succeeds only if the instance equals one of + // the listed values, so {"enum": []} is unsatisfiable (always fails). + if enum != nil { + currentSchema.enum = make([]string, 0, len(enum)) + for _, v := range enum { + is, err := marshalWithoutNumber(v) + if err != nil { + return err + } + if isStringInSlice(currentSchema.enum, *is) { + return errors.New(formatErrorDescription( + Locale.KeyItemsMustBeUnique(), + ErrorDetails{"key": KeyEnum}, + )) + } + currentSchema.enum = append(currentSchema.enum, *is) } - currentSchema.enum = append(currentSchema.enum, *is) } // validation : SubSchema diff --git a/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/schemaLoader.go b/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/schemaLoader.go index a27113d83f..50a1333178 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/schemaLoader.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/schemaLoader.go @@ -16,6 +16,7 @@ package gojsonschema import ( "bytes" + "context" "errors" "github.com/xeipuuv/gojsonreference" @@ -28,15 +29,20 @@ type SchemaLoader struct { Validate bool Draft Draft ValidatePatterns bool + // AllowNet is the list of hosts that remote references may be fetched + // from. A nil list permits any host; a non-nil empty list permits none. + AllowNet []string + // Context, when set, aborts in-flight remote reference fetches. Callers + // that have one -- evaluation, which holds the query's context -- should + // pass it so a cancelled or timed-out query doesn't leave requests + // running behind it. + Context context.Context } // NewSchemaLoader creates a new NewSchemaLoader func NewSchemaLoader() *SchemaLoader { - ps := &SchemaLoader{ - pool: &schemaPool{ - schemaPoolDocuments: make(map[string]*schemaPoolDocument), - }, + pool: &schemaPool{schemaPoolDocuments: make(map[string]*schemaPoolDocument)}, AutoDetect: true, Validate: false, Draft: Hybrid, @@ -46,15 +52,10 @@ func NewSchemaLoader() *SchemaLoader { return ps } -func (sl *SchemaLoader) validateMetaschema(documentNode any) error { - - var ( - schema string - err error - ) +func (sl *SchemaLoader) validateMetaschema(documentNode any) (err error) { + var schema string if sl.AutoDetect { - schema, _, err = parseSchemaURL(documentNode) - if err != nil { + if schema, _, err = parseSchemaURL(documentNode); err != nil { return err } } @@ -71,7 +72,6 @@ func (sl *SchemaLoader) validateMetaschema(documentNode any) error { sl.Validate = false metaSchema, err := sl.Compile(NewReferenceLoader(schema)) - if err != nil { return err } @@ -84,7 +84,7 @@ func (sl *SchemaLoader) validateMetaschema(documentNode any) error { var res bytes.Buffer for _, err := range result.Errors() { res.WriteString(err.String()) - res.WriteString("\n") + res.WriteByte('\n') } return errors.New(res.String()) } @@ -99,7 +99,6 @@ func (sl *SchemaLoader) AddSchemas(loaders ...JSONLoader) error { for _, loader := range loaders { doc, err := loader.LoadJSON() - if err != nil { return err } @@ -122,15 +121,12 @@ func (sl *SchemaLoader) AddSchemas(loaders ...JSONLoader) error { // AddSchema adds a schema under the provided URL to the schema cache func (sl *SchemaLoader) AddSchema(url string, loader JSONLoader) error { - ref, err := gojsonreference.NewJsonReference(url) - if err != nil { return err } doc, err := loader.LoadJSON() - if err != nil { return err } @@ -146,16 +142,20 @@ func (sl *SchemaLoader) AddSchema(url string, loader JSONLoader) error { // Compile loads and compiles a schema func (sl *SchemaLoader) Compile(rootSchema JSONLoader) (*Schema, error) { - ref, err := rootSchema.JSONReference() - if err != nil { return nil, err } d := Schema{} d.Pool = sl.pool - d.Pool.jsonLoaderFactory = rootSchema.LoaderFactory() + // NewStringLoader and NewGoLoader hand back unrestricted factories, so the + // limits come from the compilation, not the root loader. The pool resolves + // every $ref through this factory, however deeply nested. + d.Pool.jsonLoaderFactory = rootSchema.LoaderFactory().withRemoteRefLimits(remoteRefLimits{ + allowNet: newAllowNetSet(sl.AllowNet), + ctx: sl.Context, + }) d.DocumentReference = ref d.ReferencePool = newSchemaReferencePool() d.validatePatterns = sl.ValidatePatterns @@ -170,14 +170,12 @@ func (sl *SchemaLoader) Compile(rootSchema JSONLoader) (*Schema, error) { doc = spd.Document } else { // Load JSON directly - doc, err = rootSchema.LoadJSON() - if err != nil { + if doc, err = rootSchema.LoadJSON(); err != nil { return nil, err } // References need only be parsed if loading JSON directly - // as pool.GetDocument already does this for us if loading by reference - err = sl.pool.parseReferences(doc, ref, true) - if err != nil { + // as pool.GetDocument already does this for us if loading by reference + if err = sl.pool.parseReferences(doc, ref, true); err != nil { return nil, err } } @@ -199,8 +197,7 @@ func (sl *SchemaLoader) Compile(rootSchema JSONLoader) (*Schema, error) { } } - err = d.parse(doc, draft) - if err != nil { + if err = d.parse(doc, draft); err != nil { return nil, err } diff --git a/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/utils.go b/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/utils.go index a8639d4d9a..ee06ba9099 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/utils.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/utils.go @@ -23,7 +23,6 @@ // // created 26-02-2013 -// nolint:unused // Package in development (2021). package gojsonschema import ( diff --git a/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/validation.go b/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/validation.go index e33a0f3d27..890785a495 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/validation.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gojsonschema/validation.go @@ -419,8 +419,9 @@ func (v *SubSchema) validateCommon(currentSubSchema *SubSchema, value any, resul } } - // enum: - if len(currentSubSchema.enum) > 0 { + // enum: nil means the keyword is absent; non-nil (including empty) means + // the instance must deep-equal one of the listed values. + if currentSubSchema.enum != nil { vString, err := marshalWithoutNumber(value) if err != nil { result.addInternalError(new(InternalError), context, value, ErrorDetails{"error": err}) diff --git a/vendor/github.com/open-policy-agent/opa/internal/lcss/qsufsort.go b/vendor/github.com/open-policy-agent/opa/internal/lcss/qsufsort.go index 61c5196886..db83fec312 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/lcss/qsufsort.go +++ b/vendor/github.com/open-policy-agent/opa/internal/lcss/qsufsort.go @@ -24,7 +24,10 @@ package lcss -import "sort" +import ( + "slices" + "sort" +) // qsufsort constructs the suffix array for a given string. func qsufsort(data []byte) []int { @@ -55,7 +58,7 @@ func qsufsort(data []byte) []int { } pk := inv[s] + 1 // pk-1 is last position of unsorted group sufSortable.sa = sa[pi:pk] - sort.Sort(sufSortable) + sort.Sort(sufSortable) //nolint:forbidigo sufSortable.updateGroups(pi) pi = pk // next group } @@ -98,15 +101,15 @@ func initGroups(sa []int, data []byte) []int { inv := make([]int, len(data)) prevGroup := len(sa) - 1 groupByte := data[sa[prevGroup]] - for i := len(sa) - 1; i >= 0; i-- { - if b := data[sa[i]]; b < groupByte { + for i, s := range slices.Backward(sa) { + if b := data[s]; b < groupByte { if prevGroup == i+1 { sa[i+1] = -1 } groupByte = b prevGroup = i } - inv[sa[i]] = prevGroup + inv[s] = prevGroup if prevGroup == 0 { sa[0] = -1 } diff --git a/vendor/github.com/open-policy-agent/opa/internal/leb128/leb128.go b/vendor/github.com/open-policy-agent/opa/internal/leb128/leb128.go index 24ddc90951..03755bd506 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/leb128/leb128.go +++ b/vendor/github.com/open-policy-agent/opa/internal/leb128/leb128.go @@ -50,10 +50,7 @@ func MustReadVarUint64(r io.Reader) uint64 { // ReadVarUint32 tries to read a uint32 from r. func ReadVarUint32(r io.Reader) (uint32, error) { u64, err := ReadVarUint64(r) - if err != nil { - return 0, err - } - return uint32(u64), nil + return uint32(u64), err } // ReadVarUint64 tries to read a uint64 from r. @@ -78,10 +75,7 @@ func ReadVarUint64(r io.Reader) (uint64, error) { // ReadVarInt32 tries to read a int32 from r. func ReadVarInt32(r io.Reader) (int32, error) { i64, err := ReadVarInt64(r) - if err != nil { - return 0, err - } - return int32(i64), nil + return int32(i64), err } // ReadVarInt64 tries to read a int64 from r. diff --git a/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/funcs.go b/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/funcs.go index 4d733135fe..a313a97c85 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/funcs.go +++ b/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/funcs.go @@ -100,14 +100,6 @@ func addValueFuncs(out map[string]reflect.Value, in FuncMap) { } } -// addFuncs adds to values the functions in funcs. It does no checking of the input - -// call addValueFuncs first. -func addFuncs(out, in FuncMap) { - for name, fn := range in { - out[name] = fn - } -} - // goodFunc reports whether the function or method has the right result signature. func goodFunc(name string, typ reflect.Type) error { // We allow functions with 1 result or 2 results where the second is an error. diff --git a/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/internal/fmtsort/sort.go b/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/internal/fmtsort/sort.go index f51cdc7083..73479306c0 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/internal/fmtsort/sort.go +++ b/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/internal/fmtsort/sort.go @@ -11,7 +11,8 @@ package fmtsort import ( "cmp" "reflect" - "slices" + + "github.com/open-policy-agent/opa/v1/util" ) // Note: Throughout this package we avoid calling reflect.Value.Interface as @@ -59,10 +60,9 @@ func Sort(mapValue reflect.Value) SortedMap { for iter.Next() { sorted = append(sorted, KeyValue{iter.Key(), iter.Value()}) } - slices.SortStableFunc(sorted, func(a, b KeyValue) int { + return util.SortedStableFunc(sorted, func(a, b KeyValue) int { return compare(a.Key, b.Key) }) - return sorted } // compare compares two values of the same type. It returns -1, 0, 1 diff --git a/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/template.go b/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/template.go index 9ae5a6ca5b..5ed6225fcd 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/template.go +++ b/vendor/github.com/open-policy-agent/opa/internal/methodlesstemplate/template.go @@ -175,9 +175,9 @@ func (t *Template) Delims(left, right string) *Template { func (t *Template) Funcs(funcMap FuncMap) *Template { t.init() t.muFuncs.Lock() - defer t.muFuncs.Unlock() addValueFuncs(t.execFuncs, funcMap) - addFuncs(t.parseFuncs, funcMap) + maps.Copy(t.parseFuncs, funcMap) + t.muFuncs.Unlock() return t } diff --git a/vendor/github.com/open-policy-agent/opa/internal/planner/planner.go b/vendor/github.com/open-policy-agent/opa/internal/planner/planner.go index a7d62542c2..e5a62a2e04 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/planner/planner.go +++ b/vendor/github.com/open-policy-agent/opa/internal/planner/planner.go @@ -6,10 +6,12 @@ package planner import ( + "cmp" "errors" "fmt" "io" - "sort" + "slices" + "strings" "github.com/open-policy-agent/opa/internal/debug" "github.com/open-policy-agent/opa/v1/ast" @@ -47,6 +49,11 @@ type Planner struct { lnext ir.Local // next variable to use loc *location.Location // location currently "being planned" debug debug.Debug // debug information produced during planning + + allRules map[*ast.Rule]bool // all rules parsed from input modules, used to track unplanned rules for additional reporting (e.g. coverage) + plannedRules map[*ast.Rule]bool + + unplannedRules bool // whether to populate policy.UnplannedRules } // debugf prepends the planner location. We're passing callstack depth 2 because @@ -81,6 +88,9 @@ func New() *Planner { funcs: newFuncstack(), mocks: newFunctionMocksStack(), debug: debug.Discard(), + + allRules: map[*ast.Rule]bool{}, + plannedRules: map[*ast.Rule]bool{}, } } @@ -113,6 +123,14 @@ func (p *Planner) WithDebug(sink io.Writer) *Planner { return p } +// WithUnplannedRules controls whether the resulting policy includes the +// list of rules that were parsed but never planned (i.e. not reachable +// from any entrypoint). Disabled by default. +func (p *Planner) WithUnplannedRules(yes bool) *Planner { + p.unplannedRules = yes + return p +} + // Plan returns a IR plan for the policy query. func (p *Planner) Plan() (*ir.Policy, error) { @@ -128,9 +146,32 @@ func (p *Planner) Plan() (*ir.Policy, error) { return nil, err } + if p.unplannedRules { + p.buildUnplannedRules() + } + return p.policy, nil } +// buildUnplannedRules populates policy.UnplannedRules with the rules that +// were parsed but never planned (i.e. not reachable from any entrypoint), +// for coverage reporting purposes. +func (p *Planner) buildUnplannedRules() { + for rule := range p.allRules { + if p.plannedRules[rule] { + continue + } + p.policy.UnplannedRules = append(p.policy.UnplannedRules, &ir.UnplannedRule{ + Path: rule.Ref().String(), + Location: p.newLocation(rule.Loc()), + }) + } + + slices.SortFunc(p.policy.UnplannedRules, func(a, b *ir.UnplannedRule) int { + return strings.Compare(a.Path, b.Path) + }) +} + func (p *Planner) buildFunctrie() error { for _, module := range p.modules { @@ -149,6 +190,8 @@ func (p *Planner) buildFunctrie() error { } for _, rule := range module.Rules { + p.allRules[rule] = true + r := rule.Ref().StringPrefix() val := p.rules.LookupOrInsert(r) @@ -161,15 +204,19 @@ func (p *Planner) buildFunctrie() error { } func (p *Planner) planRules(rules []*ast.Rule) (string, error) { + for _, rule := range rules { + p.plannedRules[rule] = true + } + // We sort rules, first by ref length, and then using the // Ref.Compare method to break ties. This yields a stable // sorting order for the slice of rules to be planned. - sort.Slice(rules, func(i, j int) bool { - li, lj := len(rules[i].Ref()), len(rules[j].Ref()) - if li != lj { - return li > lj + slices.SortFunc(rules, func(a, b *ast.Rule) int { + aRef, bRef := a.Ref(), b.Ref() + if c := cmp.Compare(len(aRef), len(bRef)); c != 0 { + return -c } - return rules[i].Ref().Compare(rules[j].Ref()) < 0 + return aRef.Compare(bRef) }) // We know the rules that are closer to the root (shorter static path) are ordered first. @@ -898,8 +945,8 @@ func (p *Planner) planWith(e *ast.Expr, iter planiter) error { p.mocks.PopFrame() if shadowing { p.funcs.Pop() - for i := len(dataRefs) - 1; i >= 0; i-- { - p.rules.Pop(dataRefs[i]) + for _, dataRef := range slices.Backward(dataRefs) { + p.rules.Pop(dataRef) } } @@ -923,8 +970,8 @@ func (p *Planner) planWith(e *ast.Expr, iter planiter) error { p.mocks.PopFrame() if shadowing { p.funcs.Pop() - for i := len(dataRefs) - 1; i >= 0; i-- { - p.rules.Pop(dataRefs[i]) + for _, dataRef := range slices.Backward(dataRefs) { + p.rules.Pop(dataRef) } } return err @@ -2441,15 +2488,14 @@ func (p *Planner) planTermSliceRec(terms []*ast.Term, locals []ir.Operand, index } func (p *Planner) planExterns() error { - p.policy.Static.BuiltinFuncs = make([]*ir.BuiltinFunc, 0, len(p.externs)) for name, decl := range p.externs { p.policy.Static.BuiltinFuncs = append(p.policy.Static.BuiltinFuncs, &ir.BuiltinFunc{Name: name, Decl: decl.Decl}) } - sort.Slice(p.policy.Static.BuiltinFuncs, func(i, j int) bool { - return p.policy.Static.BuiltinFuncs[i].Name < p.policy.Static.BuiltinFuncs[j].Name + slices.SortFunc(p.policy.Static.BuiltinFuncs, func(a, b *ir.BuiltinFunc) int { + return strings.Compare(a.Name, b.Name) }) return nil @@ -2479,6 +2525,18 @@ func (p *Planner) getFileConst(s string) int { return index } +// newLocation builds a fresh *ir.Location from an ast.Location. It lives on +// Planner because it needs p.getFileConst to resolve the file constant index. +func (p *Planner) newLocation(loc *location.Location) *ir.Location { + str := loc.File + if str == "" { + str = `` + } + l := &ir.Location{} + l.SetLocation(p.getFileConst(str), loc.Row, loc.Col, str, loc.Text) + return l +} + func (p *Planner) appendStmt(s ir.Stmt) { p.appendStmtToBlock(s, p.curr) } @@ -2489,7 +2547,7 @@ func (p *Planner) appendStmtToBlock(s ir.Stmt, b *ir.Block) { if str == "" { str = `` } - s.SetLocation(p.getFileConst(str), p.loc.Row, p.loc.Col, str, string(p.loc.Text)) + s.SetLocation(p.getFileConst(str), p.loc.Row, p.loc.Col, str, p.loc.Text) } b.Stmts = append(b.Stmts, s) } diff --git a/vendor/github.com/open-policy-agent/opa/internal/planner/rules.go b/vendor/github.com/open-policy-agent/opa/internal/planner/rules.go index ed4da8571b..21245134e4 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/planner/rules.go +++ b/vendor/github.com/open-policy-agent/opa/internal/planner/rules.go @@ -2,7 +2,7 @@ package planner import ( "fmt" - "sort" + "slices" "github.com/open-policy-agent/opa/v1/ast" "github.com/open-policy-agent/opa/v1/util" @@ -242,10 +242,7 @@ func (t *ruletrie) Children() []ast.Value { sorted = append(sorted, key) } } - sort.Slice(sorted, func(i, j int) bool { - return sorted[i].Compare(sorted[j]) < 0 - }) - return sorted + return util.SortedFunc(sorted, ast.Value.Compare) } func (t *ruletrie) Get(k ast.Value) *ruletrie { @@ -326,8 +323,8 @@ func (s *functionMocksStack) PopFrame() { func (s *functionMocksStack) Lookup(f string) *ast.Term { current := s.stack.PeekGroup() - for i := len(current) - 1; i >= 0; i-- { - if t, ok := current[i][f]; ok { + for _, c := range slices.Backward(current) { + if t, ok := c[f]; ok { return t } } diff --git a/vendor/github.com/open-policy-agent/opa/internal/planner/varstack.go b/vendor/github.com/open-policy-agent/opa/internal/planner/varstack.go index 0df6bcd8b2..da70815b55 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/planner/varstack.go +++ b/vendor/github.com/open-policy-agent/opa/internal/planner/varstack.go @@ -5,6 +5,8 @@ package planner import ( + "slices" + "github.com/open-policy-agent/opa/v1/ast" "github.com/open-policy-agent/opa/v1/ir" ) @@ -34,8 +36,8 @@ func (vs varstack) GetOrEmpty(k ast.Var) ir.Local { } func (vs varstack) Get(k ast.Var) (ir.Local, bool) { - for i := len(vs) - 1; i >= 0; i-- { - if l, ok := vs[i][k]; ok { + for _, v := range slices.Backward(vs) { + if l, ok := v[k]; ok { return l, true } } diff --git a/vendor/github.com/open-policy-agent/opa/internal/providers/aws/crypto/ecc.go b/vendor/github.com/open-policy-agent/opa/internal/providers/aws/crypto/ecc.go index f93261a809..27db7645c2 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/providers/aws/crypto/ecc.go +++ b/vendor/github.com/open-policy-agent/opa/internal/providers/aws/crypto/ecc.go @@ -2,7 +2,6 @@ package crypto import ( "bytes" - "crypto/ecdh" "crypto/ecdsa" "crypto/elliptic" "crypto/hmac" @@ -31,34 +30,8 @@ func ECDSAKeyFromPoint(curve elliptic.Curve, d *big.Int) *ecdsa.PrivateKey { dBytes := make([]byte, (curve.Params().BitSize+7)/8) d.FillBytes(dBytes) - privKey := &ecdsa.PrivateKey{ - PublicKey: ecdsa.PublicKey{ - Curve: curve, - }, - D: d, - } - - var pubBytes []byte - switch curve { - case elliptic.P256(): - if ecdhPriv, err := ecdh.P256().NewPrivateKey(dBytes); err == nil { - pubBytes = ecdhPriv.PublicKey().Bytes() - } - case elliptic.P384(): - if ecdhPriv, err := ecdh.P384().NewPrivateKey(dBytes); err == nil { - pubBytes = ecdhPriv.PublicKey().Bytes() - } - case elliptic.P521(): - if ecdhPriv, err := ecdh.P521().NewPrivateKey(dBytes); err == nil { - pubBytes = ecdhPriv.PublicKey().Bytes() - } - } - - if len(pubBytes) > 0 { - byteLen := (curve.Params().BitSize + 7) / 8 - privKey.X = new(big.Int).SetBytes(pubBytes[1 : 1+byteLen]) - privKey.Y = new(big.Int).SetBytes(pubBytes[1+byteLen:]) - } else { + privKey, err := ecdsa.ParseRawPrivateKey(curve, dBytes) + if err != nil { panic(fmt.Sprintf("unsupported curve or invalid private key: %v", curve)) } @@ -67,8 +40,8 @@ func ECDSAKeyFromPoint(curve elliptic.Curve, d *big.Int) *ecdsa.PrivateKey { // mathIntToBytes writes val as a big-endian, fixed-length byte slice into out, // zero-padding on the left when val.Bytes() is shorter than out. This satisfies -// the uncompressed SEC 1 encoding (0x04 || X || Y) expected by crypto/ecdh's -// NewPublicKey: https://pkg.go.dev/crypto/ecdh#Curve.NewPublicKey +// the uncompressed SEC 1 encoding (0x04 || X || Y) expected by +// ecdsa.ParseUncompressedPublicKey: https://pkg.go.dev/crypto/ecdsa#ParseUncompressedPublicKey func mathIntToBytes(val *big.Int, out []byte) { valBytes := val.Bytes() copy(out[len(out)-len(valBytes):], valBytes) @@ -86,27 +59,12 @@ func ECDSAPublicKey(curve elliptic.Curve, x, y []byte) (*ecdsa.PublicKey, error) mathIntToBytes(xPoint, buf[1:1+byteLen]) mathIntToBytes(yPoint, buf[1+byteLen:]) - var err error - switch curve { - case elliptic.P256(): - _, err = ecdh.P256().NewPublicKey(buf) - case elliptic.P384(): - _, err = ecdh.P384().NewPublicKey(buf) - case elliptic.P521(): - _, err = ecdh.P521().NewPublicKey(buf) - default: - err = fmt.Errorf("unsupported curve for ECDSA: %v", curve) - } - + pub, err := ecdsa.ParseUncompressedPublicKey(curve, buf) if err != nil { return nil, fmt.Errorf("point(%v, %v) is not on the given curve", xPoint.String(), yPoint.String()) } - return &ecdsa.PublicKey{ - Curve: curve, - X: xPoint, - Y: yPoint, - }, nil + return pub, nil } // VerifySignature takes the provided public key, hash, and asn1 encoded signature and returns diff --git a/vendor/github.com/open-policy-agent/opa/internal/providers/aws/signing_v4.go b/vendor/github.com/open-policy-agent/opa/internal/providers/aws/signing_v4.go index c463ccbff8..cb3f57d77e 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/providers/aws/signing_v4.go +++ b/vendor/github.com/open-policy-agent/opa/internal/providers/aws/signing_v4.go @@ -151,23 +151,31 @@ func SignV4(headers map[string][]string, method string, theURL *url.URL, body [] // the "canonical request" is the normalized version of the AWS service access // that we're attempting to perform - canonicalReq := method + "\n" // HTTP method - canonicalReq += theURL.EscapedPath() + "\n" // URI-escaped path - canonicalReq += theURL.RawQuery + "\n" // RAW Query String + buf := bytes.NewBufferString(method) + buf.WriteByte('\n') + buf.WriteString(theURL.EscapedPath()) + buf.WriteByte('\n') + buf.WriteString(theURL.RawQuery) + buf.WriteByte('\n') // include the values for the signed headers orderedKeys := util.KeysSorted(headersToSign) for _, k := range orderedKeys { - // TODO: fix later - //nolint:perfsprint - canonicalReq += k + ":" + strings.Join(headersToSign[k], ",") + "\n" + buf.WriteString(k) + buf.WriteByte(':') + buf.WriteString(strings.Join(headersToSign[k], ",")) + buf.WriteByte('\n') } - canonicalReq += "\n" // linefeed to terminate headers + + buf.WriteByte('\n') // linefeed to terminate headers // include the list of the signed headers headerList := strings.Join(orderedKeys, ";") - canonicalReq += headerList + "\n" - canonicalReq += contentSha256 + buf.WriteString(headerList) + buf.WriteByte('\n') + buf.WriteString(contentSha256) + + canonicalReq := buf.String() // the "string to sign" is a time-bounded, scoped request token which // is linked to the "canonical request" by inclusion of its SHA-256 hash diff --git a/vendor/github.com/open-policy-agent/opa/internal/providers/aws/signing_v4a.go b/vendor/github.com/open-policy-agent/opa/internal/providers/aws/signing_v4a.go index db20eddc9d..0cdfb12664 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/providers/aws/signing_v4a.go +++ b/vendor/github.com/open-policy-agent/opa/internal/providers/aws/signing_v4a.go @@ -4,7 +4,6 @@ package aws import ( "bytes" "crypto" - "crypto/ecdh" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" @@ -16,7 +15,7 @@ import ( "math/big" "net/http" "net/url" - "sort" + "slices" "strconv" "strings" "sync" @@ -113,23 +112,10 @@ func deriveKeyFromAccessKeyPair(accessKey, secretKey string) (*ecdsa.PrivateKey, } d = d.Add(d, one) - priv := new(ecdsa.PrivateKey) - priv.PublicKey.Curve = p256 - priv.D = d - dBytes := make([]byte, 32) d.FillBytes(dBytes) - ecdhPriv, err := ecdh.P256().NewPrivateKey(dBytes) - if err != nil { - return nil, err - } - pubBytes := ecdhPriv.PublicKey().Bytes() - - priv.PublicKey.X = new(big.Int).SetBytes(pubBytes[1:33]) - priv.PublicKey.Y = new(big.Int).SetBytes(pubBytes[33:]) - - return priv, nil + return ecdsa.ParseRawPrivateKey(p256, dBytes) } // v4aCredentials is Context, ECDSA, and Optional Session Token that can be used @@ -211,7 +197,7 @@ func (s *httpSigner) Build() (signedRequest, error) { // Sort Each Query Key's Values for key := range query { - sort.Strings(query[key]) + slices.Sort(query[key]) } v4Internal.SanitizeHostForHeader(req) @@ -319,7 +305,7 @@ func (*httpSigner) buildCanonicalHeaders(host string, rule v4Internal.Rule, head headers = append(headers, lowerCaseKey) signed[lowerCaseKey] = v } - sort.Strings(headers) + slices.Sort(headers) signedHeaders = strings.Join(headers, ";") diff --git a/vendor/github.com/open-policy-agent/opa/internal/providers/aws/util.go b/vendor/github.com/open-policy-agent/opa/internal/providers/aws/util.go index d43339c961..b3ccdbfa0d 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/providers/aws/util.go +++ b/vendor/github.com/open-policy-agent/opa/internal/providers/aws/util.go @@ -22,7 +22,7 @@ func DoRequestWithClient(req *http.Request, client *http.Client, desc string, lo "url": req.URL.String(), "status": resp.Status, "headers": resp.Header, - }).Debug("Received response from " + desc + " service.") + }).Debug("Received response from %s service.", desc) body, err := io.ReadAll(resp.Body) if err != nil { diff --git a/vendor/github.com/open-policy-agent/opa/internal/providers/aws/v4/host.go b/vendor/github.com/open-policy-agent/opa/internal/providers/aws/v4/host.go index bf93659a43..19c689da45 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/providers/aws/v4/host.go +++ b/vendor/github.com/open-policy-agent/opa/internal/providers/aws/v4/host.go @@ -28,48 +28,37 @@ func getHost(r *http.Request) string { // If Host is an IPv6 literal with a port number, Hostname returns the // IPv6 literal without the square brackets. IPv6 literals may include // a zone identifier. -// -// Copied from the Go 1.8 standard library (net/url) func stripPort(hostport string) string { - colon := strings.IndexByte(hostport, ':') - if colon == -1 { + before, _, ok := strings.Cut(hostport, ":") + if !ok { return hostport } - if i := strings.IndexByte(hostport, ']'); i != -1 { - return strings.TrimPrefix(hostport[:i], "[") + if before, _, ok := strings.Cut(hostport, "]"); ok { + return strings.TrimPrefix(before, "[") } - return hostport[:colon] + return before } // Port returns the port part of u.Host, without the leading colon. // If u.Host doesn't contain a port, Port returns an empty string. -// -// Copied from the Go 1.8 standard library (net/url) func portOnly(hostport string) string { - colon := strings.IndexByte(hostport, ':') - if colon == -1 { + _, after, ok := strings.Cut(hostport, ":") + if !ok { return "" } - if i := strings.Index(hostport, "]:"); i != -1 { - return hostport[i+len("]:"):] + if _, after, ok := strings.Cut(hostport, "]:"); ok { + return after } if strings.Contains(hostport, "]") { return "" } - return hostport[colon+len(":"):] + return after } // Returns true if the specified URI is using the standard port // (i.e. port 80 for HTTP URIs or 443 for HTTPS URIs) func isDefaultPort(scheme, port string) bool { - if port == "" { - return true - } - - lowerCaseScheme := strings.ToLower(scheme) - if (lowerCaseScheme == "http" && port == "80") || (lowerCaseScheme == "https" && port == "443") { - return true - } - - return false + return port == "" || + (strings.EqualFold(scheme, "http") && port == "80") || + (strings.EqualFold(scheme, "https") && port == "443") } diff --git a/vendor/github.com/open-policy-agent/opa/internal/semver/semver.go b/vendor/github.com/open-policy-agent/opa/internal/semver/semver.go index d46f80aeb8..61ba1ac42e 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/semver/semver.go +++ b/vendor/github.com/open-policy-agent/opa/internal/semver/semver.go @@ -69,11 +69,9 @@ func Parse(version string) (v Version, err error) { return v, err } - if v.Patch, err = strconv.ParseInt(after, 10, 64); err != nil { - return v, err - } + v.Patch, err = strconv.ParseInt(after, 10, 64) - return v, nil + return v, err } // MustParse is like Parse but panics if the version string is invalid instead of returning an error. diff --git a/vendor/github.com/open-policy-agent/opa/internal/uuid/uuid.go b/vendor/github.com/open-policy-agent/opa/internal/uuid/uuid.go index 63e1a5b071..bdf41d7b8b 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/uuid/uuid.go +++ b/vendor/github.com/open-policy-agent/opa/internal/uuid/uuid.go @@ -5,27 +5,36 @@ package uuid import ( + "encoding/hex" "fmt" "io" "strings" "github.com/google/uuid" + "github.com/open-policy-agent/opa/v1/util" ) -const ( - BILLION = 1000000000 -) +const BILLION = 1000000000 // New Create a version 4 random UUID func New(r io.Reader) (string, error) { - bs := make([]byte, 16) - n, err := io.ReadFull(r, bs) - if n != len(bs) || err != nil { + var arr [52]byte // arr, same buffer for both src (16) and dst (36) + src := arr[:16] // src, bytes to encode + dst := arr[16:16:52] // dst, to encode, len 0, cap 36 (for appending) + + n, err := io.ReadFull(r, src) + if n != 16 || err != nil { return "", err } - bs[8] = bs[8]&^0xc0 | 0x80 - bs[6] = bs[6]&^0xf0 | 0x40 - return fmt.Sprintf("%x-%x-%x-%x-%x", bs[0:4], bs[4:6], bs[6:8], bs[8:10], bs[10:]), nil + src[8] = src[8]&^0xc0 | 0x80 + src[6] = src[6]&^0xf0 | 0x40 + + dst = append(hex.AppendEncode(dst, src[:4]), '-') + dst = append(hex.AppendEncode(dst, src[4:6]), '-') + dst = append(hex.AppendEncode(dst, src[6:8]), '-') + dst = append(hex.AppendEncode(dst, src[8:10]), '-') + + return util.ByteSliceToString(hex.AppendEncode(dst, src[10:])), nil } // Parse will use the google/uuid library to parse the string into a uuid diff --git a/vendor/github.com/open-policy-agent/opa/internal/version/version.go b/vendor/github.com/open-policy-agent/opa/internal/version/version.go index 2655c9d716..8d1237bce3 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/version/version.go +++ b/vendor/github.com/open-policy-agent/opa/internal/version/version.go @@ -7,7 +7,6 @@ package version import ( "context" - "fmt" "runtime" "github.com/open-policy-agent/opa/v1/storage" @@ -19,7 +18,6 @@ var versionPath = storage.MustParsePath("/system/version") // Write the build version information into storage. This makes the // version information available to the REPL and the HTTP server. func Write(ctx context.Context, store storage.Store, txn storage.Transaction) error { - if err := storage.MakeDir(ctx, store, txn, versionPath); err != nil { return err } @@ -33,4 +31,4 @@ func Write(ctx context.Context, store storage.Store, txn storage.Transaction) er } // UserAgent defines the current OPA instances User-Agent default header value. -var UserAgent = fmt.Sprintf("Open-Policy-Agent/%s (%s, %s)", version.Version, runtime.GOOS, runtime.GOARCH) +var UserAgent = "Open-Policy-Agent/" + version.Version + " (" + runtime.GOOS + ", " + runtime.GOARCH + ")" diff --git a/vendor/github.com/open-policy-agent/opa/internal/wasm/encoding/reader.go b/vendor/github.com/open-policy-agent/opa/internal/wasm/encoding/reader.go index a2f23d9a64..559a61696d 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/wasm/encoding/reader.go +++ b/vendor/github.com/open-policy-agent/opa/internal/wasm/encoding/reader.go @@ -651,11 +651,8 @@ func readExport(r io.Reader, exp *module.Export) error { } exp.Descriptor.Index, err = leb128.ReadVarUint32(r) - if err != nil { - return err - } - return nil + return err } func readElementSegment(r io.Reader, seg *module.ElementSegment) error { diff --git a/vendor/github.com/open-policy-agent/opa/internal/wasm/sdk/opa/capabilities/capabilities_nowasm.go b/vendor/github.com/open-policy-agent/opa/internal/wasm/sdk/opa/capabilities/capabilities_nowasm.go index 6b17984bb8..93fd1c5e80 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/wasm/sdk/opa/capabilities/capabilities_nowasm.go +++ b/vendor/github.com/open-policy-agent/opa/internal/wasm/sdk/opa/capabilities/capabilities_nowasm.go @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. //go:build !opa_wasm && !generate -// +build !opa_wasm,!generate package capabilities diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/annotations.go b/vendor/github.com/open-policy-agent/opa/v1/ast/annotations.go index 2b6a83eeeb..1aaf9680c7 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/annotations.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/annotations.go @@ -107,24 +107,18 @@ func (a *Annotations) SetLoc(l *Location) { // EndLoc returns the location of this annotation's last comment line. func (a *Annotations) EndLoc() *Location { - if a.endLoc == nil { - return a.Location - } - return a.endLoc + return util.NilOr(a.endLoc, a.Location) } // Compare returns an integer indicating if a is less than, equal to, or greater // than other. func (a *Annotations) Compare(other *Annotations) int { - - if a == nil && other == nil { + if a == other { return 0 } - if a == nil { return -1 } - if other == nil { return 1 } @@ -141,19 +135,19 @@ func (a *Annotations) Compare(other *Annotations) int { return cmp } - if cmp := compareStringLists(a.Organizations, other.Organizations); cmp != 0 { + if cmp := slices.Compare(a.Organizations, other.Organizations); cmp != 0 { return cmp } - if cmp := compareRelatedResources(a.RelatedResources, other.RelatedResources); cmp != 0 { + if cmp := slices.CompareFunc(a.RelatedResources, other.RelatedResources, (*RelatedResourceAnnotation).Compare); cmp != 0 { return cmp } - if cmp := compareAuthors(a.Authors, other.Authors); cmp != 0 { + if cmp := slices.CompareFunc(a.Authors, other.Authors, (*AuthorAnnotation).Compare); cmp != 0 { return cmp } - if cmp := compareSchemas(a.Schemas, other.Schemas); cmp != 0 { + if cmp := slices.CompareFunc(a.Schemas, other.Schemas, (*SchemaAnnotation).Compare); cmp != 0 { return cmp } @@ -172,11 +166,7 @@ func (a *Annotations) Compare(other *Annotations) int { return cmp } - if cmp := util.Compare(a.Labels, other.Labels); cmp != 0 { - return cmp - } - - return 0 + return util.Compare(a.Labels, other.Labels) } // GetTargetPath returns the path of the node these Annotations are applied to (the target) @@ -217,12 +207,10 @@ func (ar *AnnotationsRef) GetPackage() *Package { } func (ar *AnnotationsRef) GetRule() *Rule { - switch n := ar.node.(type) { - case *Rule: - return n - default: - return nil + if r, ok := ar.node.(*Rule); ok { + return r } + return nil } func scopeCompare(s1, s2 string) int { @@ -251,188 +239,81 @@ func scopeOrder(s string) int { return 0 } -func compareAuthors(a, b []*AuthorAnnotation) int { - if len(a) > len(b) { - return 1 - } else if len(a) < len(b) { - return -1 - } - - for i := range a { - if cmp := a[i].Compare(b[i]); cmp != 0 { - return cmp - } - } - - return 0 -} - -func compareRelatedResources(a, b []*RelatedResourceAnnotation) int { - if len(a) > len(b) { - return 1 - } else if len(a) < len(b) { - return -1 - } - - for i := range a { - if cmp := a[i].Compare(b[i]); cmp != 0 { - return cmp - } - } - - return 0 -} - -func compareSchemas(a, b []*SchemaAnnotation) int { - maxLen := min(len(b), len(a)) - - for i := range maxLen { - if cmp := a[i].Compare(b[i]); cmp != 0 { - return cmp - } - } - - if len(a) > len(b) { - return 1 - } else if len(a) < len(b) { - return -1 - } - - return 0 -} - -func compareStringLists(a, b []string) int { - if len(a) > len(b) { - return 1 - } else if len(a) < len(b) { - return -1 - } - - for i := range a { - if cmp := strings.Compare(a[i], b[i]); cmp != 0 { - return cmp - } - } - - return 0 -} - // Copy returns a deep copy of s. func (a *Annotations) Copy(node Node) *Annotations { cpy := *a - - cpy.Organizations = make([]string, len(a.Organizations)) - copy(cpy.Organizations, a.Organizations) - - cpy.RelatedResources = make([]*RelatedResourceAnnotation, len(a.RelatedResources)) - for i := range a.RelatedResources { - cpy.RelatedResources[i] = a.RelatedResources[i].Copy() - } - - cpy.Authors = make([]*AuthorAnnotation, len(a.Authors)) - for i := range a.Authors { - cpy.Authors[i] = a.Authors[i].Copy() - } - - cpy.Schemas = make([]*SchemaAnnotation, len(a.Schemas)) - for i := range a.Schemas { - cpy.Schemas[i] = a.Schemas[i].Copy() - } - + cpy.Organizations = slices.Clone(a.Organizations) + cpy.RelatedResources = util.Map(a.RelatedResources, (*RelatedResourceAnnotation).Copy) + cpy.Authors = util.Map(a.Authors, (*AuthorAnnotation).Copy) + cpy.Schemas = util.Map(a.Schemas, (*SchemaAnnotation).Copy) cpy.Compile = a.Compile.Copy() if a.Custom != nil { cpy.Custom = deepcopy.Map(a.Custom) } - if a.Labels != nil { cpy.Labels = deepcopy.Map(a.Labels) } - cpy.node = node return &cpy } -// toObject constructs an AST Object from the annotation. -func (a *Annotations) toObject() (*Object, *Error) { - obj := NewObject() - +// toTerm constructs an AST Object from the annotation, and wraps it in a *Term. +func (a *Annotations) toTerm() (*Term, *Error) { if a == nil { - return &obj, nil - } + return ObjectTerm(), nil + } + + items := make([][2]*Term, 0, util.Count(util.Identity, + a.Entrypoint, + len(a.Scope) > 0, + len(a.Title) > 0, + len(a.Description) > 0, + len(a.Organizations) > 0, + len(a.RelatedResources) > 0, + len(a.Authors) > 0, + len(a.Schemas) > 0, + len(a.Custom) > 0, + len(a.Labels) > 0, + )) if len(a.Scope) > 0 { - obj.Insert(InternedTerm("scope"), InternedTerm(a.Scope)) + items = append(items, [2]*Term{InternedTerm("scope"), InternedTerm(a.Scope)}) } if len(a.Title) > 0 { - obj.Insert(InternedTerm("title"), StringTerm(a.Title)) + items = append(items, [2]*Term{InternedTerm("title"), StringTerm(a.Title)}) } if a.Entrypoint { - obj.Insert(InternedTerm("entrypoint"), InternedTerm(true)) + items = append(items, [2]*Term{InternedTerm("entrypoint"), InternedTerm(true)}) } if len(a.Description) > 0 { - obj.Insert(InternedTerm("description"), StringTerm(a.Description)) + items = append(items, [2]*Term{InternedTerm("description"), StringTerm(a.Description)}) } if len(a.Organizations) > 0 { - orgs := make([]*Term, 0, len(a.Organizations)) - for _, org := range a.Organizations { - orgs = append(orgs, StringTerm(org)) - } - obj.Insert(InternedTerm("organizations"), ArrayTerm(orgs...)) + items = append(items, [2]*Term{InternedTerm("organizations"), ArrayTerm(util.Map(a.Organizations, StringTerm)...)}) } if len(a.RelatedResources) > 0 { - rrs := make([]*Term, 0, len(a.RelatedResources)) - for _, rr := range a.RelatedResources { - rrObj := NewObject(Item(InternedTerm("ref"), StringTerm(rr.Ref.String()))) - if len(rr.Description) > 0 { - rrObj.Insert(InternedTerm("description"), StringTerm(rr.Description)) - } - rrs = append(rrs, NewTerm(rrObj)) - } - obj.Insert(InternedTerm("related_resources"), ArrayTerm(rrs...)) + rrs := util.Map(a.RelatedResources, (*RelatedResourceAnnotation).toTerm) + items = append(items, [2]*Term{InternedTerm("related_resources"), ArrayTerm(rrs...)}) } if len(a.Authors) > 0 { - as := make([]*Term, 0, len(a.Authors)) - for _, author := range a.Authors { - aObj := NewObject() - if len(author.Name) > 0 { - aObj.Insert(InternedTerm("name"), StringTerm(author.Name)) - } - if len(author.Email) > 0 { - aObj.Insert(InternedTerm("email"), StringTerm(author.Email)) - } - as = append(as, NewTerm(aObj)) - } - obj.Insert(InternedTerm("authors"), ArrayTerm(as...)) + as := util.Map(a.Authors, (*AuthorAnnotation).toTerm) + items = append(items, [2]*Term{InternedTerm("authors"), ArrayTerm(as...)}) } if len(a.Schemas) > 0 { - ss := make([]*Term, 0, len(a.Schemas)) - for _, s := range a.Schemas { - sObj := NewObject() - if len(s.Path) > 0 { - sObj.Insert(InternedTerm("path"), NewTerm(s.Path.toArray())) - } - if len(s.Schema) > 0 { - sObj.Insert(InternedTerm("schema"), NewTerm(s.Schema.toArray())) - } - if s.Definition != nil { - def, err := InterfaceToValue(s.Definition) - if err != nil { - return nil, NewError(CompileErr, a.Location, "invalid definition in schema annotation: %s", err.Error()) - } - sObj.Insert(InternedTerm("definition"), NewTerm(def)) - } - ss = append(ss, NewTerm(sObj)) + ss, err := util.TryMap(a.Schemas, (*SchemaAnnotation).toTerm) + if err != nil { + return nil, NewError(CompileErr, a.Location, "invalid schema annotation %s", err.Error()) } - obj.Insert(InternedTerm("schemas"), ArrayTerm(ss...)) + items = append(items, [2]*Term{InternedTerm("schemas"), ArrayTerm(ss...)}) } if len(a.Custom) > 0 { @@ -440,7 +321,7 @@ func (a *Annotations) toObject() (*Object, *Error) { if err != nil { return nil, NewError(CompileErr, a.Location, "invalid custom annotation %s", err.Error()) } - obj.Insert(InternedTerm("custom"), NewTerm(c)) + items = append(items, [2]*Term{InternedTerm("custom"), NewTerm(c)}) } if len(a.Labels) > 0 { @@ -448,10 +329,10 @@ func (a *Annotations) toObject() (*Object, *Error) { if err != nil { return nil, NewError(CompileErr, a.Location, "invalid labels annotation %s", err.Error()) } - obj.Insert(InternedTerm("labels"), NewTerm(l)) + items = append(items, [2]*Term{InternedTerm("labels"), NewTerm(l)}) } - return &obj, nil + return ObjectTerm(items...), nil } func attachRuleAnnotations(mod *Module) { @@ -484,13 +365,11 @@ func attachRuleAnnotations(mod *Module) { func attachAnnotationsNodes(mod *Module) Errors { var errs Errors - // Find first non-annotation statement following each annotation and attach // the annotation to that statement. for _, a := range mod.Annotations { for _, stmt := range mod.stmts { - _, ok := stmt.(*Annotations) - if !ok { + if _, ok := stmt.(*Annotations); !ok { if stmt.Loc().Row > a.Location.Row { a.node = stmt break @@ -501,10 +380,9 @@ 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 @@ -530,7 +408,6 @@ func attachAnnotationsNodes(mod *Module) Errors { } func validateAnnotationScopeAttachment(a *Annotations) *Error { - switch a.Scope { case annotationScopeRule, annotationScopeDocument: if _, ok := a.node.(*Rule); ok { @@ -568,12 +445,7 @@ func (a *AuthorAnnotation) Compare(other *AuthorAnnotation) int { if cmp := strings.Compare(a.Name, other.Name); cmp != 0 { return cmp } - - if cmp := strings.Compare(a.Email, other.Email); cmp != 0 { - return cmp - } - - return 0 + return strings.Compare(a.Email, other.Email) } func (a *AuthorAnnotation) String() string { @@ -585,6 +457,17 @@ func (a *AuthorAnnotation) String() string { return fmt.Sprintf("%s <%s>", a.Name, a.Email) } +func (a *AuthorAnnotation) toTerm() *Term { + items := make([][2]*Term, 0, 2) + if len(a.Name) > 0 { + items = append(items, [2]*Term{InternedTerm("name"), StringTerm(a.Name)}) + } + if len(a.Email) > 0 { + items = append(items, [2]*Term{InternedTerm("email"), StringTerm(a.Email)}) + } + return ObjectTerm(items...) +} + // Copy returns a deep copy of rr. func (rr *RelatedResourceAnnotation) Copy() *RelatedResourceAnnotation { cpy := *rr @@ -597,12 +480,7 @@ func (rr *RelatedResourceAnnotation) Compare(other *RelatedResourceAnnotation) i if cmp := strings.Compare(rr.Description, other.Description); cmp != 0 { return cmp } - - if cmp := strings.Compare(rr.Ref.String(), other.Ref.String()); cmp != 0 { - return cmp - } - - return 0 + return strings.Compare(rr.Ref.String(), other.Ref.String()) } func (rr *RelatedResourceAnnotation) String() string { @@ -610,6 +488,17 @@ func (rr *RelatedResourceAnnotation) String() string { return string(bs) } +func (rr *RelatedResourceAnnotation) toTerm() *Term { + items := make([][2]*Term, 0, 2) + if len(rr.Ref.String()) > 0 { + items = append(items, [2]*Term{InternedTerm("ref"), StringTerm(rr.Ref.String())}) + } + if len(rr.Description) > 0 { + items = append(items, [2]*Term{InternedTerm("description"), StringTerm(rr.Description)}) + } + return ObjectTerm(items...) +} + // Copy returns a deep copy of s. func (s *SchemaAnnotation) Copy() *SchemaAnnotation { cpy := *s @@ -622,20 +511,20 @@ func (s *SchemaAnnotation) Compare(other *SchemaAnnotation) int { if cmp := s.Path.Compare(other.Path); cmp != 0 { return cmp } - if cmp := s.Schema.Compare(other.Schema); cmp != 0 { return cmp } - if s.Definition != nil && other.Definition == nil { + switch { + case s.Definition == other.Definition: + return 0 + case s.Definition == nil: return -1 - } else if s.Definition == nil && other.Definition != nil { + case other.Definition == nil: return 1 - } else if s.Definition != nil && other.Definition != nil { - return util.Compare(*s.Definition, *other.Definition) } - return 0 + return util.Compare(*s.Definition, *other.Definition) } func (s *SchemaAnnotation) String() string { @@ -643,15 +532,31 @@ func (s *SchemaAnnotation) String() string { return string(bs) } +func (s *SchemaAnnotation) toTerm() (*Term, error) { + items := make([][2]*Term, 0, 3) + if len(s.Path.String()) > 0 { + items = append(items, [2]*Term{InternedTerm("path"), NewTerm(s.Path.toArray())}) + } + if len(s.Schema.String()) > 0 { + items = append(items, [2]*Term{InternedTerm("schema"), NewTerm(s.Schema.toArray())}) + } + if s.Definition != nil { + def, err := InterfaceToValue(s.Definition) + if err != nil { + return nil, err + } + items = append(items, [2]*Term{InternedTerm("definition"), NewTerm(def)}) + } + return ObjectTerm(items...), nil +} + // Copy returns a deep copy of s. func (c *CompileAnnotation) Copy() *CompileAnnotation { if c == nil { return nil } cpy := *c - for i := range c.Unknowns { - cpy.Unknowns[i] = c.Unknowns[i].Copy() - } + cpy.Unknowns = util.Map(c.Unknowns, Ref.Copy) return &cpy } @@ -659,12 +564,12 @@ func (c *CompileAnnotation) Copy() *CompileAnnotation { // than other. func (c *CompileAnnotation) Compare(other *CompileAnnotation) int { switch { - case c == nil && other == nil: + case c == other: return 0 - case c != nil && other == nil: - return 1 - case c == nil && other != nil: + case c == nil: return -1 + case other == nil: + return 1 } if cmp := slices.CompareFunc(c.Unknowns, other.Unknowns, RefCompare); cmp != 0 { @@ -776,24 +681,18 @@ func (as *AnnotationSet) GetPackageScope(pkg *Package) *Annotations { // The returned slice is sorted, first by the annotations' target path, then by their target location func (as *AnnotationSet) Flatten() FlatAnnotationsRefSet { // This preallocation often won't be optimal, but it's superior to starting with a nil slice. - refs := make([]*AnnotationsRef, 0, len(as.byPath.Children)+len(as.byRule)+len(as.byPackage)) - - refs = as.byPath.flatten(refs) - + size := len(as.byPath.Children) + len(as.byRule) + len(as.byPackage) + refs := as.byPath.flatten(make([]*AnnotationsRef, 0, size)) for _, a := range as.byPackage { refs = append(refs, NewAnnotationsRef(a)) } for _, as := range as.byRule { - for _, a := range as { - refs = append(refs, NewAnnotationsRef(a)) - } + refs = util.MapAppend(refs, as, NewAnnotationsRef) } // Sort by path, then annotation location, for stable output - slices.SortStableFunc(refs, (*AnnotationsRef).Compare) - - return refs + return util.SortedStableFunc(refs, (*AnnotationsRef).Compare) } // Chain returns the chain of annotations leading up to the given rule. @@ -804,10 +703,7 @@ func (as *AnnotationSet) Flatten() FlatAnnotationsRefSet { // 3. Entries for the 'subpackages' scope, if any; ordered from the closest package path to the fartest. E.g.: 'do.re.mi', 'do.re', 'do' // The returned slice is guaranteed to always contain at least one entry, corresponding to the given rule. func (as *AnnotationSet) Chain(rule *Rule) AnnotationsRefSet { - var refs []*AnnotationsRef - ruleAnnots := as.GetRuleScope(rule) - // Fall back to the rule's own attached annotations when the rule's source // module isn't tracked by this AnnotationSet. This happens for rules // supplied by an ExternalRuleSource that returns []*Rule directly: their @@ -819,10 +715,12 @@ func (as *AnnotationSet) Chain(rule *Rule) AnnotationsRefSet { ruleAnnots = rule.Annotations } + var refs []*AnnotationsRef if len(ruleAnnots) >= 1 { - for _, a := range ruleAnnots { - refs = append(refs, NewAnnotationsRef(a)) - } + // Sort by annotation location; chain must start with annotations declared closest to rule, then going outward + refs = util.SortedStableFunc(util.Map(ruleAnnots, NewAnnotationsRef), func(a, b *AnnotationsRef) int { + return -a.Annotations.Location.Compare(b.Annotations.Location) + }) } else { // Make sure there is always a leading entry representing the passed rule, even if it has no annotations refs = append(refs, &AnnotationsRef{ @@ -832,29 +730,19 @@ func (as *AnnotationSet) Chain(rule *Rule) AnnotationsRefSet { }) } - if len(refs) > 1 { - // Sort by annotation location; chain must start with annotations declared closest to rule, then going outward - slices.SortStableFunc(refs, func(a, b *AnnotationsRef) int { - return -a.Annotations.Location.Compare(b.Annotations.Location) - }) - } - - docAnnots := as.GetDocumentScope(rule.Ref().GroundPrefix()) - if docAnnots != nil { - refs = append(refs, NewAnnotationsRef(docAnnots)) + if da := as.GetDocumentScope(rule.Ref().GroundPrefix()); da != nil { + refs = append(refs, NewAnnotationsRef(da)) } - pkg := rule.Module.Package - pkgAnnots := as.GetPackageScope(pkg) - if pkgAnnots != nil { - refs = append(refs, NewAnnotationsRef(pkgAnnots)) + if pa := as.GetPackageScope(rule.Module.Package); pa != nil { + refs = append(refs, NewAnnotationsRef(pa)) } - subPkgAnnots := as.GetSubpackagesScope(pkg.Path) + subPkgAnnots := as.GetSubpackagesScope(rule.Module.Package.Path) // We need to reverse the order, as subPkgAnnots ordering will start at the root, // whereas we want to end at the root. - for i := len(subPkgAnnots) - 1; i >= 0; i-- { - refs = append(refs, NewAnnotationsRef(subPkgAnnots[i])) + for _, subPkgAnnot := range slices.Backward(subPkgAnnots) { + refs = append(refs, NewAnnotationsRef(subPkgAnnot)) } return refs @@ -880,8 +768,8 @@ func (as *AnnotationSet) MergedLabels(rule *Rule) (labels map[string]any, key st // we iterate in reverse to fold outer-to-inner. func mergeChainLabels(chain AnnotationsRefSet) map[string]any { var merged map[string]any - for i := len(chain) - 1; i >= 0; i-- { - a := chain[i].Annotations + for _, c := range slices.Backward(chain) { + a := c.Annotations if a == nil || len(a.Labels) == 0 { continue } @@ -899,8 +787,7 @@ func (ars FlatAnnotationsRefSet) Insert(ar *AnnotationsRef) FlatAnnotationsRefSe // insertion sort, first by path, then location for i, current := range ars { if ar.Compare(current) < 0 { - result = append(result, ar) - result = append(result, ars[i:]...) + result = append(append(result, ar), ars[i:]...) break } result = append(result, current) @@ -981,10 +868,8 @@ func (ar *AnnotationsRef) Compare(other *AnnotationsRef) int { if c := ar.Path.Compare(other.Path); c != 0 { return c } - if c := ar.Annotations.Location.Compare(other.Annotations.Location); c != 0 { return c } - return ar.Annotations.Compare(other.Annotations) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/builtins.go b/vendor/github.com/open-policy-agent/opa/v1/ast/builtins.go index 17ed06035d..1f92cadd1d 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/builtins.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/builtins.go @@ -941,18 +941,20 @@ var ArrayReverse = &Builtin{ var conversions = category("conversions") var ToNumber = &Builtin{ - Name: "to_number", - Description: "Converts a string, bool, or number value to a number: Strings are converted to numbers using `strconv.Atoi`, Boolean `false` is converted to 0 and `true` is converted to 1.", + Name: "to_number", + Description: "Converts value of type string, null or boolean to number. Numeric strings converts to the " + + "corresponding number when possible. Null and boolean `false` converts to 0 and boolean `true` to 1. " + + "Numbers are returned without conversion.", Decl: types.NewFunction( types.Args( - types.Named("x", types.NewAny( + types.Named("value", types.NewAny( types.N, types.S, types.B, types.Nl, )).Description("value to convert"), ), - types.Named("num", types.N).Description("the numeric representation of `x`"), + types.Named("num", types.N).Description("the numeric representation of `value`"), ), Categories: conversions, CanSkipBctx: true, @@ -1002,7 +1004,7 @@ var RegexFindAllStringSubmatch = &Builtin{ var RegexTemplateMatch = &Builtin{ Name: "regex.template_match", - Description: "Matches a string against a pattern, where there pattern may be glob-like", + Description: "Matches a string against a pattern, where the pattern may be glob-like", Decl: types.NewFunction( types.Args( types.Named("template", types.S).Description("template expression containing `0..n` regular expressions"), @@ -3123,8 +3125,10 @@ var JSONSchemaVerify = &Builtin{ }, nil)). Description("`output` is of the form `[valid, error]`. If the schema is valid, then `valid` is `true`, and `error` is `null`. Otherwise, `valid` is `false` and `error` is a string describing the error."), ), - Categories: objectCat, - CanSkipBctx: true, + Categories: objectCat, + // Needs the BuiltinContext to read the allow_net capability, which + // restricts the hosts that remote `$ref`s may be fetched from. + CanSkipBctx: false, } // JSONMatchSchema returns empty array if the document matches the JSON schema, @@ -3676,12 +3680,8 @@ func (b *Builtin) IsNondeterministic() bool { func (b *Builtin) Expr(operands ...*Term) *Expr { ts := make([]*Term, len(operands)+1) ts[0] = NewTerm(b.Ref()) - for i := range operands { - ts[i+1] = operands[i] - } - return &Expr{ - Terms: ts, - } + copy(ts[1:], operands) + return &Expr{Terms: ts} } // Call creates a new term for the built-in with the given operands. diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/capabilities.go b/vendor/github.com/open-policy-agent/opa/v1/ast/capabilities.go index 0c37dfb1fe..e804147d69 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/capabilities.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/capabilities.go @@ -10,9 +10,9 @@ import ( "encoding/json" "fmt" "io" + "io/fs" "os" "slices" - "sort" "strings" "sync" @@ -188,8 +188,8 @@ func CapabilitiesForThisVersion(opts ...CapabilitiesOption) *Capabilities { copy(f.Features, Features) } - sort.Strings(f.FutureKeywords) - sort.Strings(f.Features) + slices.Sort(f.FutureKeywords) + slices.Sort(f.Features) return f } @@ -239,14 +239,7 @@ func LoadCapabilitiesVersions() ([]string, error) { return nil, err } - capabilitiesVersions := make([]string, 0, len(ents)) - for _, ent := range ents { - capabilitiesVersions = append(capabilitiesVersions, strings.Replace(ent.Name(), ".json", "", 1)) - } - - slices.SortStableFunc(capabilitiesVersions, semver.Compare) - - return capabilitiesVersions, nil + return util.SortedStableFunc(util.Map(ents, removeJsonSuffix), semver.Compare), nil } // MinimumCompatibleVersion returns the minimum compatible OPA version based on @@ -306,14 +299,18 @@ func (c *Capabilities) ContainsFutureKeyword(kw string) bool { // addBuiltinSorted inserts a built-in into c in sorted order. An existing built-in with the same name // will be overwritten. func (c *Capabilities) addBuiltinSorted(bi *Builtin) { - i := sort.Search(len(c.Builtins), func(x int) bool { - return c.Builtins[x].Name >= bi.Name - }) - if i < len(c.Builtins) && bi.Name == c.Builtins[i].Name { - c.Builtins[i] = bi - return + i, found := slices.BinarySearchFunc(c.Builtins, bi, cmpBuiltinName) + if !found { + c.Builtins = append(c.Builtins, nil) + copy(c.Builtins[i+1:], c.Builtins[i:]) } - c.Builtins = append(c.Builtins, nil) - copy(c.Builtins[i+1:], c.Builtins[i:]) c.Builtins[i] = bi } + +func cmpBuiltinName(a, b *Builtin) int { + return strings.Compare(a.Name, b.Name) +} + +func removeJsonSuffix(ent fs.DirEntry) string { + return strings.Replace(ent.Name(), ".json", "", 1) +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/check.go b/vendor/github.com/open-policy-agent/opa/v1/ast/check.go index ed3d14a15f..c07130d205 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/check.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/check.go @@ -16,6 +16,10 @@ import ( type varRewriter func(Ref) Ref +// dependentsResolver returns the refs of the rules that (transitively) depend on +// the document(s) at ref. +type dependentsResolver func(Ref) []Ref + // typeChecker implements type checking on queries and rules. Errors are // accumulated on the typeChecker so that a single run can report multiple // issues. @@ -29,6 +33,8 @@ type typeChecker struct { input types.Type allowUndefinedFuncs bool schemaTypes map[string]types.Type + dependentsResolver dependentsResolver + withTrees map[string]*typeTreeNode } // newTypeChecker returns a new typeChecker object that has no errors. @@ -56,6 +62,7 @@ func (tc *typeChecker) copy() *typeChecker { WithInputType(tc.input). WithAllowUndefinedFunctionCalls(tc.allowUndefinedFuncs). WithBuiltins(tc.builtins). + WithDependentsResolver(tc.dependentsResolver). WithRequiredCapabilities(tc.required) } @@ -89,6 +96,13 @@ func (tc *typeChecker) WithVarRewriter(f varRewriter) *typeChecker { return tc } +// WithDependentsResolver sets the function used to look up the rules that depend +// on the document(s) replaced by a with modifier. +func (tc *typeChecker) WithDependentsResolver(f dependentsResolver) *typeChecker { + tc.dependentsResolver = f + return tc +} + func (tc *typeChecker) WithInputType(tpe types.Type) *typeChecker { tc.input = tpe return tc @@ -124,17 +138,27 @@ func (tc *typeChecker) CheckBody(env *TypeEnv, body Body) (*TypeEnv, Errors) { for _, bexpr := range body { WalkExprs(bexpr, func(expr *Expr) bool { - closureErrs := tc.checkClosures(env, expr) + exprEnv, exprVis, exprGV := env, vis, gv + + if len(expr.With) > 0 { + if withEnv := tc.withEnv(env, expr); withEnv != env { + exprEnv = withEnv + exprVis = newRefChecker(withEnv, tc.varRewriter) + exprGV = NewGenericVisitor(exprVis.Visit) + } + } + + closureErrs := tc.checkClosures(exprEnv, expr) errors = append(errors, closureErrs...) // reset errors from previous iteration - vis.errs = nil - gv.Walk(expr) - errors = append(errors, vis.errs...) + exprVis.errs = nil + exprGV.Walk(expr) + errors = append(errors, exprVis.errs...) - if err := tc.checkExpr(env, expr); err != nil { + if err := tc.checkExpr(exprEnv, expr); err != nil { hasClosureErrors := len(closureErrs) > 0 - hasRefErrors := len(vis.errs) > 0 + hasRefErrors := len(exprVis.errs) > 0 // Suppress this error if a more actionable one has occurred. In // this case, if an error occurred in a ref or closure contained in // this expression, and the error is due to a nil type, then it's @@ -270,10 +294,8 @@ func (tc *typeChecker) checkRule(env *TypeEnv, as *AnnotationSet, rule *Rule) { for _, arg := range rule.Head.Args { // If args are not referred to in body, infer as any. WalkTerms(arg, func(t *Term) bool { - if _, ok := t.Value.(Var); ok { - if cpy.GetByValue(t.Value) == nil { - cpy.tree.PutOne(t.Value, types.A) - } + if _, ok := t.Value.(Var); ok && cpy.GetByValue(t.Value) == nil { + cpy.vars.PutOne(t.Value, types.A) } return false }) @@ -481,6 +503,99 @@ func checkExprEq(env *TypeEnv, expr *Expr) *Error { return nil } +// withEnv returns the TypeEnv to check expr against, where the documents its +// with modifiers replace can also have the type of their replacement value, and +// the rules depending on those documents are widened to any. +func (tc *typeChecker) withEnv(env *TypeEnv, expr *Expr) *TypeEnv { + cpy := env + targets := make([]Ref, 0, len(expr.With)) + targetTypes := make([]types.Type, 0, len(expr.With)) + + for _, w := range expr.With { + target, ok := w.Target.Value.(Ref) + if !ok { + continue + } + + targetType := env.GetByRef(target) + + // Keeping the declaration of a replaced function allows its arity to be + // checked against the replacement. + _, isFunc := targetType.(*types.Function) + + if tree := tc.relaxedDependents(target, isFunc); tree != nil { + layer := cpy.wrapWith() + // Shared with every other expression replacing this target. + layer.tree = tree + cpy = layer + } + + if !isFunc { + // A non-ground target replaces an unknown part of the document, so + // nothing more specific than any can be said about its prefix. + tpe := types.A + if target.IsGround() && targetType != nil { + // Or returns nil if only one of the two is a function. + if valueType := env.GetByValue(w.Value.Value); valueType != nil { + if or := types.Or(targetType, valueType); or != nil { + tpe = or + } + } + } + targets = append(targets, target.GroundPrefix()) + targetTypes = append(targetTypes, tpe) + } + } + + if len(targets) == 0 { + return cpy + } + + // Wrapped last, so that a replaced document keeps the type of its + // replacement value even if another modifier replaces one of its dependencies. + cpy = cpy.wrapWith() + for i := range targets { + cpy.tree.Put(targets[i], targetTypes[i]) + } + + return cpy +} + +// relaxedDependents returns a cached type tree where the rules affected by +// replacing the document(s) at target are typed as any, or nil if there are none. +func (tc *typeChecker) relaxedDependents(target Ref, isFunc bool) *typeTreeNode { + if tc.dependentsResolver == nil { + return nil + } + + key := target.String() + if isFunc { + key = "f:" + key + } + + if tree, ok := tc.withTrees[key]; ok { + return tree + } + + var tree *typeTreeNode + for _, ref := range tc.dependentsResolver(target) { + if isFunc && ref.Equal(target) { + continue + } + if tree == nil { + tree = newTypeTree() + } + tree.Put(ref, types.A) + } + + if tc.withTrees == nil { + tc.withTrees = map[string]*typeTreeNode{} + } + tc.withTrees[key] = tree + + return tree +} + func (tc *typeChecker) checkExprWith(env *TypeEnv, expr *Expr, i int) *Error { if i == len(expr.With) { return nil @@ -655,12 +770,12 @@ func unify1(env *TypeEnv, term *Term, tpe types.Type, union bool) bool { return unifies(env.GetByValue(v), tpe) case Var: if !union { - if exist := env.GetByValue(v); exist != nil { + if exist := env.GetByValue(term.Value); exist != nil { return unifies(exist, tpe) } - env.tree.PutOne(term.Value, tpe) + env.vars.PutOne(term.Value, tpe) } else { - env.tree.PutOne(term.Value, types.Or(env.GetByValue(v), tpe)) + env.vars.PutOne(term.Value, types.Or(env.GetByValue(term.Value), tpe)) } return true default: @@ -807,7 +922,7 @@ func (rc *refChecker) checkRef(curr *TypeEnv, node *typeTreeNode, ref Ref, idx i return newRefErrInvalid(ref[0].Location, rc.varRewriter(ref), idx, exist, tpe, getOneOfForNode(node)) } } else { - rc.env.tree.PutOne(head.Value, tpe) + rc.env.vars.PutOne(head.Value, tpe) } } @@ -861,7 +976,7 @@ func (rc *refChecker) checkRefLeaf(tpe types.Type, ref Ref, idx int) *Error { return newRefErrInvalid(ref[0].Location, rc.varRewriter(ref), idx, exist, keys, getOneOfForType(tpe)) } } else { - rc.env.tree.PutOne(head.Value, types.Keys(tpe)) + rc.env.vars.PutOne(head.Value, types.Keys(tpe)) } case Ref: @@ -955,9 +1070,10 @@ func unifies(a, b types.Type) bool { // NOTE(sr): variadic functions can only be internal ones, and we've forbidden // their replacement via `with`; so we disregard variadic here if types.Arity(a) == types.Arity(b) { - b := b.(*types.Function) - for i := range a.FuncArgs().Args { - if !unifies(a.FuncArgs().Arg(i), b.FuncArgs().Arg(i)) { + aArgs := a.FuncArgs() + bArgs := b.(*types.Function).FuncArgs() + for i := range aArgs.Args { + if !unifies(aArgs.Arg(i), bArgs.Arg(i)) { return false } } @@ -1190,14 +1306,8 @@ func newArgError(loc *Location, builtinName Ref, msg string, have []types.Type, return err } -func getOneOfForNode(node *typeTreeNode) (result []Value) { - node.Children().Iter(func(k Value, _ *typeTreeNode) bool { - result = append(result, k) - return false - }) - - slices.SortFunc(result, Value.Compare) - return result +func getOneOfForNode(node *typeTreeNode) []Value { + return util.SortedFunc(node.Children().Keys(), Value.Compare) } func getOneOfForType(tpe types.Type) (result []Value) { @@ -1221,9 +1331,7 @@ func getOneOfForType(tpe types.Type) (result []Value) { } } - result = removeDuplicate(result) - slices.SortFunc(result, Value.Compare) - return result + return util.SortedFunc(removeDuplicate(result), Value.Compare) } func removeDuplicate(list []Value) []Value { @@ -1273,16 +1381,11 @@ func override(ref Ref, t types.Type, o types.Type, rule *Rule) (types.Type, *Err } obj, ok := t.(*types.Object) if !ok { - newType, err := getObjectType(ref, o, rule, dynamicAnyAny) - if err != nil { - return nil, err - } - return newType, nil + return getObjectType(ref, o, rule, dynamicAnyAny) } found := false if ok { - staticProps := obj.StaticProperties() - for _, prop := range staticProps { + for _, prop := range obj.StaticProperties() { valueCopy := prop.Value key, err := InterfaceToValue(prop.Key) if err != nil { @@ -1329,8 +1432,7 @@ func getKeys(ref Ref, rule *Rule) ([]any, *Error) { func getObjectTypeRec(keys []any, o types.Type, d *types.DynamicProperty) *types.Object { if len(keys) == 1 { - staticProps := []*types.StaticProperty{types.NewStaticProperty(keys[0], o)} - return types.NewObject(staticProps, d) + return types.NewObject([]*types.StaticProperty{types.NewStaticProperty(keys[0], o)}, d) } staticProps := []*types.StaticProperty{types.NewStaticProperty(keys[0], getObjectTypeRec(keys[1:], o, d))} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/compare.go b/vendor/github.com/open-policy-agent/opa/v1/ast/compare.go index ef1ba033fd..81038e7a68 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/compare.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/compare.go @@ -8,7 +8,10 @@ import ( "cmp" "fmt" "math/big" + "slices" "strings" + + "github.com/open-policy-agent/opa/v1/util" ) // Compare returns an integer indicating whether two AST values are less than, @@ -244,38 +247,6 @@ func sortOrder(x any) int { panic(fmt.Sprintf("illegal value: %T", x)) } -func importsCompare(a, b []*Import) int { - minLen := min(len(b), len(a)) - for i := range minLen { - if cmp := a[i].Compare(b[i]); cmp != 0 { - return cmp - } - } - if len(a) < len(b) { - return -1 - } - if len(b) < len(a) { - return 1 - } - return 0 -} - -func annotationsCompare(a, b []*Annotations) int { - minLen := min(len(b), len(a)) - for i := range minLen { - if cmp := a[i].Compare(b[i]); cmp != 0 { - return cmp - } - } - if len(a) < len(b) { - return -1 - } - if len(b) < len(a) { - return 1 - } - return 0 -} - func rulesCompare(a, b []*Rule) int { minLen := min(len(b), len(a)) for i := range minLen { @@ -346,6 +317,8 @@ func ValueEqual(a, b Value) bool { return v.Equal(b) case *Array: return v.Equal(b) + case *object: + return v.Equal(b) case *Not: return v.Equal(b) case *TemplateString: @@ -360,42 +333,47 @@ func RefCompare(a, b Ref) int { } func RefEqual(a, b Ref) bool { - return termSliceEqual(a, b) + return slices.EqualFunc(a, b, (*Term).Equal) } func NumberCompare(x, y Number) int { xs, ys := string(x), string(y) - - var xIsF, yIsF bool - - // Treat "1" and "1.0", "1.00", etc as "1" - if strings.Contains(xs, ".") { - if tx := strings.TrimRight(xs, ".0"); tx != xs { - // Still a float after trimming? - xIsF = strings.Contains(tx, ".") - xs = tx - } - } - if strings.Contains(ys, ".") { - if ty := strings.TrimRight(ys, ".0"); ty != ys { - yIsF = strings.Contains(ty, ".") - ys = ty - } - } if xs == ys { return 0 } var xi, yi int64 - var xf, yf float64 var xiOK, yiOK, xfOK, yfOK bool - if xi, xiOK = x.Int64(); xiOK { - if yi, yiOK = y.Int64(); yiOK { + if xi, xiOK = util.Atoi64(xs); xiOK { + if yi, yiOK = util.Atoi64(ys); yiOK { return cmp.Compare(xi, yi) } } + var xf, yf float64 + var xIsF, yIsF bool + + // Treat "1" and "1.0", "1.00", etc as "1" for the purpose of deciding + // whether each side is a non-integral value. + // + // The trimmed forms must not be assigned back over xs and ys. TrimRight + // takes a cutset rather than a suffix, so ".0" strips every trailing '.' + // and '0' character: "0.0" trims to the empty string and "-0.0" to "-". + // Those are then handed to big.Float.SetString below, which fails, and the + // failure path is a panic. + if strings.IndexByte(xs, '.') != -1 { + if tx := strings.TrimRight(xs, ".0"); tx != xs { + // Still a float after trimming? + xIsF = strings.IndexByte(tx, '.') != -1 + } + } + if strings.IndexByte(ys, '.') != -1 { + if ty := strings.TrimRight(ys, ".0"); ty != ys { + yIsF = strings.IndexByte(ty, '.') != -1 + } + } + if xIsF && yIsF { if xf, xfOK = x.Float64(); xfOK { if yf, yfOK = y.Float64(); yfOK { @@ -408,7 +386,7 @@ func NumberCompare(x, y Number) int { } var a *big.Rat - fa, ok := new(big.Float).SetString(string(x)) + fa, ok := new(big.Float).SetString(xs) if !ok { panic("illegal value") } @@ -418,14 +396,14 @@ func NumberCompare(x, y Number) int { } } if a == nil { - a, ok = new(big.Rat).SetString(string(x)) + a, ok = new(big.Rat).SetString(xs) if !ok { panic("illegal value") } } var b *big.Rat - fb, ok := new(big.Float).SetString(string(y)) + fb, ok := new(big.Float).SetString(ys) if !ok { panic("illegal value") } @@ -435,7 +413,7 @@ func NumberCompare(x, y Number) int { } } if b == nil { - b, ok = new(big.Rat).SetString(string(y)) + b, ok = new(big.Rat).SetString(ys) if !ok { panic("illegal value") } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/compile.go b/vendor/github.com/open-policy-agent/opa/v1/ast/compile.go index c642c508a8..f40835ed74 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/compile.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/compile.go @@ -11,7 +11,6 @@ import ( "io" "maps" "slices" - "sort" "strings" "sync" @@ -29,8 +28,7 @@ const CompileErrorLimitDefault = 10 var ( errLimitReached = newErrorString(CompileErr, nil, "error limit reached") - - doubleEq = Equal.Ref() + emptyPackage = &Package{Path: Ref{VarTerm("")}} ) // Compiler contains the state of a compilation process. @@ -468,7 +466,7 @@ func NewCompiler() *Compiler { {StageCheckSafetyRuleHeads, "compile_stage_check_safety_rule_heads", c.checkSafetyRuleHeads}, {StageCheckSafetyRuleBodies, "compile_stage_check_safety_rule_bodies", c.checkSafetyRuleBodies}, {StageRewriteEquals, "compile_stage_rewrite_equals", c.rewriteEquals}, - {StageRewriteDynamicTerms, "compile_stage_rewrite_dynamic_terms", c.rewriteDynamicTerms}, + {StageRewriteDynamicTerms, "compile_stage_rewrite_dynamic_terms", c.rewriteDynamicTerms}, // stages before CheckTypes must not rewrite hoisted terms, see recordSubjectNoCopy {StageRewriteTestRulesForTracing, "compile_stage_rewrite_test_rules_for_tracing", c.rewriteTestRuleEqualities}, // must run after RewriteDynamicTerms {StageCheckRecursion, "compile_stage_check_recursion", c.checkRecursion}, {StageCheckTypes, "compile_stage_check_types", c.checkTypes}, // must be run after CheckRecursion @@ -672,7 +670,7 @@ func (c *Compiler) Compile(modules map[string]*Module) { c.init() c.Modules = make(map[string]*Module, len(modules)) - c.sorted = make([]string, 0, len(modules)) + c.sorted = util.KeysSorted(modules) if c.keepModules { c.parsedModules = make(map[string]*Module, len(modules)) @@ -682,14 +680,11 @@ func (c *Compiler) Compile(modules map[string]*Module) { for k, v := range modules { c.Modules[k] = v.Copy() - c.sorted = append(c.sorted, k) if c.parsedModules != nil { c.parsedModules[k] = v } } - sort.Strings(c.sorted) - c.compile() } @@ -840,8 +835,7 @@ func (c *Compiler) GetRulesWithPrefix(ref Ref) (rules []*Rule) { // GetRules("data.a.b.c.q") => [rule2] // GetRules("data.a.b.c") => [rule1, rule2] // GetRules("data.a.b.d") => nil -func (c *Compiler) GetRules(ref Ref) (rules []*Rule) { - +func (c *Compiler) GetRules(ref Ref) []*Rule { set := map[*Rule]struct{}{} for _, rule := range c.GetRulesForVirtualDocument(ref) { @@ -852,11 +846,7 @@ func (c *Compiler) GetRules(ref Ref) (rules []*Rule) { set[rule] = struct{}{} } - for rule := range set { - rules = append(rules, rule) - } - - return rules + return util.Keys(set) } // GetRulesDynamic returns a slice of rules that could be referred to by a ref. @@ -950,11 +940,7 @@ func (c *Compiler) GetRulesDynamicWithOpts(ref Ref, opts RulesOptions) []*Rule { } walk(node, 0) - rules := make([]*Rule, 0, len(set)) - for rule := range set { - rules = append(rules, rule) - } - return rules + return util.Keys(set) } // Utility: add all rule values to the set. @@ -1109,9 +1095,7 @@ func (c *Compiler) buildExecutionPlan() *executionPlan { // getOrBuildPlan ensures we have a valid execution plan. func (c *Compiler) getOrBuildPlan() *executionPlan { - if c.plan == nil { - c.plan = c.buildExecutionPlan() - } + c.plan = util.Or(c.plan, c.buildExecutionPlan) return c.plan } @@ -1223,7 +1207,6 @@ func (c *Compiler) buildRequiredCapabilities() { if c.moduleIsRegoV1(c.Modules[name]) { for kw := range futureKeywords { // Don't output experimental keywords for wildcard imports - // TODO: Remove on and/or release if _, internal := experimentalFutureKeywords[kw]; internal { continue } @@ -1231,7 +1214,6 @@ func (c *Compiler) buildRequiredCapabilities() { } } else { for kw := range allFutureKeywords { - // TODO: Remove on and/or release if _, internal := experimentalFutureKeywords[kw]; internal { continue } @@ -1494,8 +1476,7 @@ func (c *Compiler) checkRuleConflicts() { func (c *Compiler) checkUndefinedFuncs() { for _, name := range c.sorted { - m := c.Modules[name] - c.err(checkUndefinedFuncs(c.TypeEnv, m, c.GetArity, c.RewrittenVars)...) + c.err(checkUndefinedFuncs(c.TypeEnv, c.Modules[name], c.GetArity, c.RewrittenVars)...) } } @@ -1555,6 +1536,7 @@ func (c *Compiler) checkSafetyRuleBodies() { for _, name := range c.sorted { m := c.Modules[name] + scopes := ruleScopes{module: m} WalkRules(m, func(r *Rule) bool { vis = vis.Clear() // vis.vars == safe @@ -1562,7 +1544,7 @@ func (c *Compiler) checkSafetyRuleBodies() { if len(r.Head.Args) > 0 { vis.WalkArgs(r.Head.Args) } - r.Body = c.checkBodySafety(vis.vars, r.Body) + r.Body = c.checkBodySafety(vis.vars, r.Body, r, &scopes) return false }) } @@ -1570,9 +1552,12 @@ func (c *Compiler) checkSafetyRuleBodies() { varVisitorPool.Put(vis) } -func (c *Compiler) checkBodySafety(safe VarSet, b Body) Body { +func (c *Compiler) checkBodySafety(safe VarSet, b Body, r *Rule, scopes *ruleScopes) Body { reordered, unsafe := reorderBodyForSafety(c.builtins, c.GetArity, safe, b) - if errs := safetyErrorSlice(unsafe, c.RewrittenVars); len(errs) > 0 { + if len(unsafe) == 0 { + return reordered + } + if errs := safetyErrorSlice(unsafe, c.RewrittenVars, scopes.scope(r)); len(errs) > 0 { c.err(errs...) return b } @@ -1594,7 +1579,9 @@ func (c *Compiler) checkSafetyRuleHeads() { vis := varVisitorPool.Get() for _, name := range c.sorted { - WalkRules(c.Modules[name], func(r *Rule) bool { + m := c.Modules[name] + scopes := ruleScopes{module: m} + WalkRules(m, func(r *Rule) bool { if headMayHaveVars(r.Head) { vis = vis.Clear().WithParams(SafetyCheckVisitorParams) vis.WalkBody(r.Body) @@ -1607,6 +1594,7 @@ func (c *Compiler) checkSafetyRuleHeads() { vars := r.Head.Vars() if vars.DiffCount(vis.vars) > 0 { unsafe := vars.Diff(vis.vars) + scope := scopes.scope(r) for v := range unsafe { // vars is keyed by the original name, so the location must be // read before v is replaced with the rewritten one -- otherwise @@ -1616,7 +1604,7 @@ func (c *Compiler) checkSafetyRuleHeads() { v = w } if !v.IsGenerated() { - if !c.err(NewError(UnsafeVarErr, loc, "var %v is unsafe", v)) { + if !c.err(NewError(UnsafeVarErr, loc, "var %v is unsafe%v", v, scope)) { return true } } @@ -1631,10 +1619,9 @@ func (c *Compiler) checkSafetyRuleHeads() { } func compileSchema(goSchema any, allowNet []string) (*gojsonschema.Schema, error) { - gojsonschema.SetAllowNet(allowNet) - var refLoader gojsonschema.JSONLoader sl := gojsonschema.NewSchemaLoader() + sl.AllowNet = allowNet if goSchema != nil { refLoader = gojsonschema.NewGoLoader(goSchema) @@ -1906,7 +1893,8 @@ func (c *Compiler) checkTypes() { WithInputType(c.inputType). WithBuiltins(c.builtins). WithRequiredCapabilities(c.Required). - WithVarRewriter(rewriteVarsInRef(c.RewrittenVars)). + WithVarRewriter(rewriteRefErrVars(c.localvargen.subjects, c.RewrittenVars)). + WithDependentsResolver(c.dependentRuleRefs). WithAllowUndefinedFunctionCalls(c.allowUndefinedFuncCalls) var as *AnnotationSet if c.useTypeCheckAnnotations { @@ -1919,6 +1907,40 @@ func (c *Compiler) checkTypes() { c.TypeEnv = env } +// dependentRuleRefs returns the refs of the rules that ref could refer to, +// together with the refs of the rules that transitively depend on them. +func (c *Compiler) dependentRuleRefs(ref Ref) []Ref { + if c.Graph == nil { + return nil + } + + rules := c.GetRulesDynamicWithOpts(ref, RulesOptions{IncludeHiddenModules: true}) + if len(rules) == 0 { + return nil + } + + refs := make([]Ref, 0, len(rules)) + visited := make(map[*Rule]struct{}, len(rules)) + + var visit func(*Rule) + visit = func(rule *Rule) { + if _, ok := visited[rule]; ok { + return + } + visited[rule] = struct{}{} + refs = append(refs, rule.Ref().GroundPrefix()) + for dependent := range c.Graph.Dependents(rule) { + visit(dependent.(*Rule)) + } + } + + for _, rule := range rules { + visit(rule) + } + + return refs +} + func (c *Compiler) checkUnsafeBuiltins() { if len(c.unsafeBuiltinsMap) == 0 { return @@ -1946,10 +1968,7 @@ func (c *Compiler) checkDeprecatedBuiltins() { for _, name := range c.sorted { if c.strict || c.Modules[name].regoV1Compatible() { - errs := checkDeprecatedBuiltins(c.deprecatedBuiltinsMap, c.Modules[name]) - for _, err := range errs { - c.err(err) - } + c.err(checkDeprecatedBuiltins(c.deprecatedBuiltinsMap, c.Modules[name])...) } } } @@ -2246,7 +2265,6 @@ func (c *Compiler) resolveAllRefs() { } if c.moduleLoader != nil { - parsed, err := c.moduleLoader(c.Modules) if err != nil { c.err(newErrorString(CompileErr, nil, err.Error())) @@ -2265,7 +2283,7 @@ func (c *Compiler) resolveAllRefs() { } } - sort.Strings(c.sorted) + slices.Sort(c.sorted) c.resolveAllRefs() } } @@ -2675,11 +2693,12 @@ func (c *Compiler) rewritePrintCalls() { } bodyVis := func(b Body) bool { - modrec, errs := rewritePrintCalls(c.localvargen, c.GetArity, vis.vars, b) + modrec, errs := rewritePrintCalls(c.localvargen, c.GetArity, vis.vars, c.RewrittenVars, b) if modrec { modified = true } - if !c.err(errs...) { + if len(errs) > 0 { + c.err(errs...) return true } return false @@ -2725,15 +2744,15 @@ func checkVoidCalls(env *TypeEnv, x any) Errors { // The expression would be rewritten to: // // print({__local0__ | __local0__ = "the value of x is:"}, {__local1__ | __local1__ = input.x}) -func rewritePrintCalls(gen *localVarGenerator, getArity func(Ref) int, globals VarSet, body Body) (bool, Errors) { +func rewritePrintCalls(gen *localVarGenerator, getArity func(Ref) int, globals VarSet, rewritten map[Var]Var, body Body) (bool, Errors) { var errs Errors var modified bool - // Visit comprehension bodies recursively to ensure print statements inside - // those bodies only close over variables that are safe. + // Visit nested bodies recursively to ensure print statements inside those + // bodies only close over variables that are safe. for i := range body { - if ContainsClosures(body[i]) { + if containsNestedBody(body[i]) { safe := outputVarsForBody(body[:i], getArity, globals, nil) safe.Update(globals) WalkClosures(body[i], func(x any) bool { @@ -2741,28 +2760,28 @@ func rewritePrintCalls(gen *localVarGenerator, getArity func(Ref) int, globals V var errsrec Errors switch x := x.(type) { case *SetComprehension: - modrec, errsrec = rewritePrintCalls(gen, getArity, safe, x.Body) + modrec, errsrec = rewritePrintCalls(gen, getArity, safe, rewritten, x.Body) case *ArrayComprehension: - modrec, errsrec = rewritePrintCalls(gen, getArity, safe, x.Body) + modrec, errsrec = rewritePrintCalls(gen, getArity, safe, rewritten, x.Body) case *ObjectComprehension: - modrec, errsrec = rewritePrintCalls(gen, getArity, safe, x.Body) + modrec, errsrec = rewritePrintCalls(gen, getArity, safe, rewritten, x.Body) case *Every: safe.Update(x.KeyValueVars()) - modrec, errsrec = rewritePrintCalls(gen, getArity, safe, x.Body) + modrec, errsrec = rewritePrintCalls(gen, getArity, safe, rewritten, x.Body) case *Not: - modrec, errsrec = rewritePrintCalls(gen, getArity, safe, x.Body) + modrec, errsrec = rewritePrintCalls(gen, getArity, safe, rewritten, x.Body) case *LogicalAnd: var modR bool var errsR Errors - modrec, errsrec = rewritePrintCalls(gen, getArity, safe, x.Lhs) - modR, errsR = rewritePrintCalls(gen, getArity, safe, x.Rhs) + modrec, errsrec = rewritePrintCalls(gen, getArity, safe, rewritten, x.Lhs) + modR, errsR = rewritePrintCalls(gen, getArity, safe, rewritten, x.Rhs) modrec = modrec || modR errsrec = append(errsrec, errsR...) case *LogicalOr: var modR bool var errsR Errors - modrec, errsrec = rewritePrintCalls(gen, getArity, safe, x.Lhs) - modR, errsR = rewritePrintCalls(gen, getArity, safe, x.Rhs) + modrec, errsrec = rewritePrintCalls(gen, getArity, safe, rewritten, x.Lhs) + modR, errsR = rewritePrintCalls(gen, getArity, safe, rewritten, x.Rhs) modrec = modrec || modR errsrec = append(errsrec, errsR...) } @@ -2814,6 +2833,9 @@ func rewritePrintCalls(gen *localVarGenerator, getArity func(Ref) int, globals V if vars.DiffCount(safe) > 0 { unsafe := vars.Diff(safe) for _, v := range unsafe.Sorted() { + if w, ok := rewritten[v]; ok { + v = w + } errs = append(errs, NewError(CompileErr, args[j].Loc(), "var %v is undeclared", v)) } } @@ -2840,6 +2862,18 @@ func rewritePrintCalls(gen *localVarGenerator, getArity func(Ref) int, globals V return modified, nil } +// containsNestedBody returns true if x contains any node that carries a nested +// body which rewritePrintCalls needs to descend into. This is a superset of +// ContainsClosures, which ignores not/and/or expressions. +func containsNestedBody(x any) bool { + found := false + WalkClosures(x, func(any) bool { + found = true + return found + }) + return found +} + func erasePrintCalls(node any) bool { var modified bool NewGenericVisitor(func(x any) bool { @@ -2916,10 +2950,8 @@ func containsPrintCall(x any) bool { return found } -var printRef = Print.Ref() - func isPrintCall(x *Expr) bool { - return x.IsCall() && x.Operator().Equal(printRef) + return x.IsCall() && x.Operator().Equal(Interned.Refs.Print) } // rewriteRefsInHead will rewrite rules so that the head does not contain any @@ -3093,16 +3125,15 @@ func (c *Compiler) rewriteRegoMetadataCalls() { var metadataRuleVar Var if ruleCalled { // Create and inject metadata for rule - var metadataRuleTerm *Term a := getPrimaryRuleAnnotations(c.annotationSet, rule) if a != nil { - annotObj, err := a.toObject() + annotObj, err := a.toTerm() if err != nil { return !c.err(err) } - metadataRuleTerm = NewTerm(*annotObj) + metadataRuleTerm = annotObj } else { // If rule has no annotations, assign an empty object metadataRuleTerm = ObjectTerm() @@ -3131,17 +3162,14 @@ func (c *Compiler) rewriteRegoMetadataCalls() { func getPrimaryRuleAnnotations(as *AnnotationSet, rule *Rule) *Annotations { annots := as.GetRuleScope(rule) - if len(annots) == 0 { return nil } - // Sort by annotation location; chain must start with annotations declared closest to rule, then going outward - slices.SortStableFunc(annots, func(a, b *Annotations) int { - return -a.Location.Compare(b.Location) + // chain must start with annotations declared closest to rule, then going outward + return slices.MinFunc(annots, func(a, b *Annotations) int { + return a.Location.Compare(b.Location) }) - - return annots[0] } func rewriteRegoMetadataCalls(metadataChainVar *Var, metadataRuleVar *Var, body Body, rewrittenVars *map[Var]Var) Errors { @@ -3200,30 +3228,26 @@ func rewriteRegoMetadataCalls(metadataChainVar *Var, metadataRuleVar *Var, body return errs } -var regoMetadataChainRef = RegoMetadataChain.Ref() -var regoMetadataRuleRef = RegoMetadataRule.Ref() - func isRegoMetadataChainCall(x *Expr) bool { - return x.IsCall() && x.Operator().Equal(regoMetadataChainRef) + return x.IsCall() && Interned.Refs.RegoMetadataChain.Equal(x.Operator()) } func isRegoMetadataRuleCall(x *Expr) bool { - return x.IsCall() && x.Operator().Equal(regoMetadataRuleRef) + return x.IsCall() && Interned.Refs.RegoMetadataRule.Equal(x.Operator()) } func createMetadataChain(chain []*AnnotationsRef) (*Term, *Error) { - metaArray := NewArray() for _, link := range chain { // Dropping leading 'data' element of path p := link.Path[1:].toArray() obj := NewObject(Item(InternedTerm("path"), NewTerm(p))) if link.Annotations != nil { - annotObj, err := link.Annotations.toObject() + annotObj, err := link.Annotations.toTerm() if err != nil { return nil, err } - obj.Insert(InternedTerm("annotations"), NewTerm(*annotObj)) + obj.Insert(InternedTerm("annotations"), annotObj) } metaArray = metaArray.Append(NewTerm(obj)) } @@ -3625,6 +3649,7 @@ type queryCompiler struct { qctx *QueryContext typeEnv *TypeEnv rewritten map[Var]Var + refSubjects map[Var]Value after map[string][]QueryCompilerStageDefinition unsafeBuiltins map[string]struct{} comprehensionIndices map[*Term]*ComprehensionIndex @@ -3724,7 +3749,7 @@ func (qc *queryCompiler) Compile(query Body) (Body, error) { {StageRewriteWithValues, "query_compile_stage_rewrite_with_values", qc.rewriteWithModifiers}, {StageCheckUndefinedFuncs, "query_compile_stage_check_undefined_funcs", qc.checkUndefinedFuncs}, {StageCheckSafety, "query_compile_stage_check_safety", qc.checkSafety}, - {StageRewriteDynamicTerms, "query_compile_stage_rewrite_dynamic_terms", qc.rewriteDynamicTerms}, + {StageRewriteDynamicTerms, "query_compile_stage_rewrite_dynamic_terms", qc.rewriteDynamicTerms}, // see recordSubjectNoCopy {StageCheckTypes, "query_compile_stage_check_types", qc.checkTypes}, {StageCheckUnsafeBuiltins, "query_compile_stage_check_unsafe_builtins", qc.checkUnsafeBuiltins}, {StageCheckDeprecatedBuiltins, "query_compile_stage_check_deprecated_builtins", qc.checkDeprecatedBuiltins}, @@ -3757,8 +3782,7 @@ func (qc *queryCompiler) TypeEnv() *TypeEnv { } func (qc *queryCompiler) applyErrorLimit(err error) error { - var errs Errors - if errors.As(err, &errs) { + if errs, ok := errors.AsType[Errors](err); ok { if qc.compiler.maxErrs > 0 && len(errs) > qc.compiler.maxErrs { err = append(errs[:qc.compiler.maxErrs], errLimitReached) } @@ -3776,7 +3800,6 @@ func (qc *queryCompiler) checkKeywordOverrides(_ *QueryContext, body Body) (Body } func (qc *queryCompiler) resolveRefs(qctx *QueryContext, body Body) (Body, error) { - var globals map[Var]*usedRef if qctx != nil { @@ -3784,7 +3807,7 @@ func (qc *queryCompiler) resolveRefs(qctx *QueryContext, body Body) (Body, error // Query compiler ought to generate a package if one was not provided and one or more imports were provided. // The generated package name could even be an empty string to avoid conflicts (it doesn't have to be valid syntactically) if pkg == nil && len(qctx.Imports) > 0 { - pkg = &Package{Path: RefTerm(VarTerm("")).Value.(Ref)} + pkg = emptyPackage } if pkg != nil { var ruleExports []Ref @@ -3813,15 +3836,19 @@ func (*queryCompiler) rewriteComprehensionTerms(_ *QueryContext, body Body) (Bod return node.(Body), nil } -func (*queryCompiler) rewriteDynamicTerms(_ *QueryContext, body Body) (Body, error) { +func (qc *queryCompiler) rewriteDynamicTerms(_ *QueryContext, body Body) (Body, error) { gen := newLocalVarGenerator("q", body) f := newEqualityFactory(gen) - return rewriteDynamics(f, body), nil + body = rewriteDynamics(f, body) + qc.refSubjects = mergeRefSubjects(qc.refSubjects, gen.subjects) + return body, nil } -func (*queryCompiler) rewriteExprTerms(_ *QueryContext, body Body) (Body, error) { +func (qc *queryCompiler) rewriteExprTerms(_ *QueryContext, body Body) (Body, error) { gen := newLocalVarGenerator("q", body) - return rewriteExprTermsInBody(gen, body), nil + body = rewriteExprTermsInBody(gen, body) + qc.refSubjects = gen.subjects + return body, nil } func (qc *queryCompiler) rewriteLocalVars(_ *QueryContext, body Body) (Body, error) { @@ -3855,7 +3882,7 @@ func (qc *queryCompiler) rewritePrintCalls(_ *QueryContext, body Body) (Body, er return cpy, nil } gen := newLocalVarGenerator("q", body) - if _, errs := rewritePrintCalls(gen, qc.compiler.GetArity, ReservedVars, body); len(errs) > 0 { + if _, errs := rewritePrintCalls(gen, qc.compiler.GetArity, ReservedVars, qc.RewrittenVars(), body); len(errs) > 0 { return nil, errs } return body, nil @@ -3878,7 +3905,7 @@ func (qc *queryCompiler) checkUndefinedFuncs(_ *QueryContext, body Body) (Body, func (qc *queryCompiler) checkSafety(_ *QueryContext, body Body) (Body, error) { safe := ReservedVars.Copy() reordered, unsafe := reorderBodyForSafety(qc.compiler.builtins, qc.compiler.GetArity, safe, body) - if errs := safetyErrorSlice(unsafe, qc.RewrittenVars()); len(errs) > 0 { + if errs := safetyErrorSlice(unsafe, qc.RewrittenVars(), ""); len(errs) > 0 { return nil, errs } return reordered, nil @@ -3889,7 +3916,8 @@ func (qc *queryCompiler) checkTypes(_ *QueryContext, body Body) (Body, error) { checker := newTypeChecker(). WithSchemaSet(qc.compiler.schemaSet). WithInputType(qc.compiler.inputType). - WithVarRewriter(rewriteVarsInRef(qc.rewritten, qc.compiler.RewrittenVars)) + WithDependentsResolver(qc.compiler.dependentRuleRefs). + WithVarRewriter(rewriteRefErrVars(qc.refSubjects, qc.rewritten, qc.compiler.RewrittenVars)) qc.typeEnv, errs = checker.CheckBody(qc.compiler.TypeEnv, body) if len(errs) > 0 { return nil, errs @@ -4278,13 +4306,13 @@ func (n *ModuleTreeNode) DepthFirst(f func(*ModuleTreeNode) bool) { // TreeNode represents a node in the rule tree. The rule tree is keyed by // rule path. type TreeNode struct { + Values []*Rule + Sorted []Value Key Value External *ExternalIndex - Values []*Rule + Index RuleIndex Children map[Value]*TreeNode - Sorted []Value Hide bool - Index RuleIndex } func (n *TreeNode) String() string { @@ -4450,18 +4478,13 @@ type legacyExternalResolver struct { inner ValueResolver } -func (r legacyExternalResolver) Resolve(ref Ref) (Value, error) { +func (r legacyExternalResolver) Resolve(ref Ref) (v Value, err error) { if !ref.HasPrefix(InputRootRef) { - return nil, UnknownValueErr{} + err = UnknownValueErr{} + } else if v, err = r.inner.Resolve(ref); err == nil && v == nil { + err = UnknownValueErr{} } - v, err := r.inner.Resolve(ref) - if err != nil { - return nil, err - } - if v == nil { - return nil, UnknownValueErr{} - } - return v, nil + return v, err } // unknownResolver treats every reference as unknown. It is used as a safe @@ -4839,33 +4862,33 @@ func sortGraphNodes(nodes []util.T) { }) } -func (sort *graphSort) Marked(node util.T) bool { - _, marked := sort.marked[node] +func (gs *graphSort) Marked(node util.T) bool { + _, marked := gs.marked[node] return marked } -func (sort *graphSort) Visit(node util.T) (ok bool) { - if _, ok := sort.temp[node]; ok { +func (gs *graphSort) Visit(node util.T) (ok bool) { + if _, ok := gs.temp[node]; ok { return false } - if sort.Marked(node) { + if gs.Marked(node) { return true } - sort.temp[node] = struct{}{} - deps := sort.deps(node) + gs.temp[node] = struct{}{} + deps := gs.deps(node) depList := make([]util.T, 0, len(deps)) for other := range deps { depList = append(depList, other) } sortGraphNodes(depList) for _, other := range depList { - if !sort.Visit(other) { + if !gs.Visit(other) { return false } } - sort.marked[node] = struct{}{} - delete(sort.temp, node) - sort.sorted = append(sort.sorted, node) + gs.marked[node] = struct{}{} + delete(gs.temp, node) + gs.sorted = append(gs.sorted, node) return true } @@ -5504,6 +5527,38 @@ type localVarGenerator struct { exclude VarSet suffix string next int + + // subjects maps a generated local back to the original term it replaced, + // so type errors can render the original expression (e.g. [1, 2][i] + // instead of __local0__[i]). Populated lazily. + subjects map[Var]Value +} + +// recordSubject records that local stands in for value. The value is copied, as +// stages running between the caller and CheckTypes may rewrite it in place: a +// composite subject recorded in RewriteExprTerms, say [x, input.y][i], has its +// dynamic elements hoisted by the later RewriteDynamicTerms stage, which would +// otherwise turn the recorded value into [__local5__, __local6__]. +func (l *localVarGenerator) recordSubject(local Var, value *Term) { + l.putSubject(local, CopyValue(value.Value)) +} + +// recordSubjectNoCopy records that local stands in for value, aliasing value +// rather than copying it. Only callers in the RewriteDynamicTerms stage may use +// this: only RewriteTestRulesForTracing and CheckRecursion run between that +// stage and CheckTypes, and neither rewrites hoisted terms, so nothing can +// mutate value before the mapping is read. Copying here instead would allocate +// on every hoisted ref of every compile, for a map only read when a type error +// is rendered. +func (l *localVarGenerator) recordSubjectNoCopy(local Var, value *Term) { + l.putSubject(local, value.Value) +} + +func (l *localVarGenerator) putSubject(local Var, value Value) { + if l.subjects == nil { + l.subjects = map[Var]Value{} + } + l.subjects[local] = value } func newLocalVarGeneratorForModuleSet(sorted []string, modules map[string]*Module) *localVarGenerator { @@ -5840,8 +5895,8 @@ func resolveRefsInTermSlice(globals map[Var]*usedRef, ignore *declaredVarStack, type declaredVarStack []VarSet func (s declaredVarStack) Contains(v Var) bool { - for i := len(s) - 1; i >= 0; i-- { - if _, ok := s[i][v]; ok { + for _, v0 := range slices.Backward(s) { + if _, ok := v0[v]; ok { return ok } } @@ -5960,11 +6015,12 @@ func rewriteComprehensionTerms(f *equalityFactory, node any) (any, error) { // partial evaluation cases we do want to rewrite == to = to simplify the // result. func rewriteEquals(x any) (modified bool) { + // Note: can't use Interned.Refs.Equality here as this may be mutated unifyOp := Equality.Ref() t := NewGenericTransformer(func(x any) (any, error) { if x, ok := x.(*Expr); ok && x.IsCall() { operator := x.Operator() - if operator.Equal(doubleEq) && len(x.Operands()) == 2 { + if operator.Equal(Interned.Refs.Equal) && len(x.Operands()) == 2 { modified = true x.SetOperator(NewTerm(unifyOp)) } @@ -6127,6 +6183,7 @@ func rewriteDynamicsOne(original *Expr, f *equalityFactory, term *Term, result B generated.With = original.With result.Append(generated) connectGeneratedExprs(original, generated) + f.gen.recordSubjectNoCopy(generated.Operand(0).Value.(Var), term) return result, result[len(result)-1].Operand(0) case *Array: for i := range v.Len() { @@ -6156,18 +6213,21 @@ func rewriteDynamicsOne(original *Expr, f *equalityFactory, term *Term, result B v.Body, extra = rewriteDynamicsComprehensionBody(original, f, v.Body, term) result.Append(extra) connectGeneratedExprs(original, extra) + f.gen.recordSubjectNoCopy(extra.Operand(0).Value.(Var), term) return result, result[len(result)-1].Operand(0) case *SetComprehension: var extra *Expr v.Body, extra = rewriteDynamicsComprehensionBody(original, f, v.Body, term) result.Append(extra) connectGeneratedExprs(original, extra) + f.gen.recordSubjectNoCopy(extra.Operand(0).Value.(Var), term) return result, result[len(result)-1].Operand(0) case *ObjectComprehension: var extra *Expr v.Body, extra = rewriteDynamicsComprehensionBody(original, f, v.Body, term) result.Append(extra) connectGeneratedExprs(original, extra) + f.gen.recordSubjectNoCopy(extra.Operand(0).Value.(Var), term) return result, result[len(result)-1].Operand(0) } return result, term @@ -6378,7 +6438,25 @@ func expandExprTerm(gen *localVarGenerator, term *Term) (support []*Expr, output func expandExprRef(gen *localVarGenerator, v []*Term) (support []*Expr) { // Start by calling a normal expandExprTerm on all terms. - support = expandExprTermSlice(gen, v) + for i := range v { + // A call in a ref, e.g. the opa.runtime() in opa.runtime()[0].foo, is + // hoisted into a generated local by expandExprTerm below. Record the + // call that local stands in for, so type errors on this ref render the + // call rather than the local. The call has to be copied first: + // expandExprTerm hoists nested calls out of its arguments in place. + var subject Value + if call, ok := v[i].Value.(Call); ok { + subject = call.Copy() + } + + var extras []*Expr + extras, v[i] = expandExprTerm(gen, v[i]) + support = append(support, extras...) + + if local, ok := v[i].Value.(Var); ok && subject != nil { + gen.putSubject(local, subject) + } + } // Rewrite references in order to support indirect references. We rewrite // e.g. @@ -6399,6 +6477,7 @@ func expandExprRef(gen *localVarGenerator, v []*Term) (support []*Expr) { assignToLocal := f.Generate(subject) support = append(support, assignToLocal) v[0] = assignToLocal.Operand(0) + gen.recordSubject(v[0].Value.(Var), subject) } return } @@ -6412,15 +6491,6 @@ func expandExprTermArray(gen *localVarGenerator, arr *Array) (support []*Expr) { return } -func expandExprTermSlice(gen *localVarGenerator, v []*Term) (support []*Expr) { - for i := range v { - var extras []*Expr - extras, v[i] = expandExprTerm(gen, v[i]) - support = append(support, extras...) - } - return -} - type localDeclaredVars struct { vars []*declaredVarSet @@ -6487,9 +6557,7 @@ func (s *localDeclaredVars) Clear() { if vs != nil { s.vars = append(s.vars, vs.clear()) } - if s.vars[0] == nil { - s.vars[0] = newDeclaredVarSet() - } + s.vars[0] = util.Or(s.vars[0], newDeclaredVarSet) s.assignment = false } @@ -6541,8 +6609,8 @@ func (s localDeclaredVars) Insert(x, y Var, occurrence varOccurrence) { } func (s localDeclaredVars) Declared(x Var) (y Var, ok bool) { - for i := len(s.vars) - 1; i >= 0; i-- { - if y, ok = s.vars[i].vs[x]; ok { + for _, v := range slices.Backward(s.vars) { + if y, ok = v.vs[x]; ok { return } } @@ -6558,8 +6626,8 @@ func (s localDeclaredVars) Occurrence(x Var) varOccurrence { // GlobalOccurrence returns a flag that indicates whether x has occurred in the // global scope. func (s localDeclaredVars) GlobalOccurrence(x Var) (varOccurrence, bool) { - for i := len(s.vars) - 1; i >= 0; i-- { - if occ, ok := s.vars[i].occurrence[x]; ok { + for _, v := range slices.Backward(s.vars) { + if occ, ok := v.occurrence[x]; ok { return occ, true } } @@ -6568,8 +6636,8 @@ func (s localDeclaredVars) GlobalOccurrence(x Var) (varOccurrence, bool) { // Seen marks x as seen by incrementing its counter func (s localDeclaredVars) Seen(x Var) { - for i := len(s.vars) - 1; i >= 0; i-- { - dvs := s.vars[i] + for _, dvs := range slices.Backward(s.vars) { + if c, ok := dvs.count[x]; ok { dvs.count[x] = c + 1 return @@ -6581,8 +6649,8 @@ func (s localDeclaredVars) Seen(x Var) { // Count returns how many times x has been seen func (s localDeclaredVars) Count(x Var) int { - for i := len(s.vars) - 1; i >= 0; i-- { - if c, ok := s.vars[i].count[x]; ok { + for _, v := range slices.Backward(s.vars) { + if c, ok := v.count[x]; ok { return c } } @@ -6619,8 +6687,7 @@ func rewriteDeclaredVarsInBody(g *localVarGenerator, stack *localDeclaredVars, u expr, errs = rewriteSomeDeclStatement(g, stack, body[i], errs, strict) case body[i].IsEvery(): expr, errs = rewriteEveryStatement(g, stack, body[i], errs, strict) - case body[i].IsNot() && body[i].Terms.(*Not).ExplicitBody: - // Only explicit not bodies are allowed to declare vars + case body[i].IsNot(): expr, errs = rewriteNotStatement(g, stack, body[i], errs, strict) case body[i].IsAnd() || body[i].IsOr(): expr, errs = rewriteLogicalStatement(g, stack, body[i], errs, strict) @@ -6863,7 +6930,23 @@ func rewriteSomeDeclStatement(g *localVarGenerator, stack *localDeclaredVars, ex return nil, errs } +const ( + errAssignInNegated = "cannot assign vars inside negated expression" + errAssignInAndOperand = "cannot assign vars inside implicit and operand" + errAssignInOrOperand = "cannot assign vars inside implicit or operand" +) + func rewriteNotStatement(g *localVarGenerator, stack *localDeclaredVars, expr *Expr, errs Errors, strict bool) (*Expr, Errors) { + if not := expr.Terms.(*Not); !not.ExplicitBody { + // Only explicit not bodies are allowed to declare vars. + numErrsBefore := len(errs) + errs = rewriteDeclaredVarsInImplicitBody(g, stack, not.Body, errAssignInNegated, errs, strict) + if len(errs) > numErrsBefore { + return expr, errs + } + return rewriteDeclaredVarsInExpr(g, stack, expr, errs, strict) + } + e := expr.Copy() not := e.Terms.(*Not) @@ -6879,26 +6962,33 @@ func rewriteNotStatement(g *localVarGenerator, stack *localDeclaredVars, expr *E func rewriteLogicalStatement(g *localVarGenerator, stack *localDeclaredVars, expr *Expr, errs Errors, strict bool) (*Expr, Errors) { e := expr.Copy() + numErrsBefore := len(errs) + switch t := e.Terms.(type) { case *LogicalAnd: - if t.ExplicitLhs { - t.Lhs, errs = rewriteLogicalOperandBody(g, stack, t.Lhs, errs, strict) - } - if t.ExplicitRhs { - t.Rhs, errs = rewriteLogicalOperandBody(g, stack, t.Rhs, errs, strict) - } + t.Lhs, errs = rewriteLogicalOperand(g, stack, t.Lhs, t.ExplicitLhs, errAssignInAndOperand, errs, strict) + t.Rhs, errs = rewriteLogicalOperand(g, stack, t.Rhs, t.ExplicitRhs, errAssignInAndOperand, errs, strict) case *LogicalOr: - if t.ExplicitLhs { - t.Lhs, errs = rewriteLogicalOperandBody(g, stack, t.Lhs, errs, strict) - } - if t.ExplicitRhs { - t.Rhs, errs = rewriteLogicalOperandBody(g, stack, t.Rhs, errs, strict) - } + t.Lhs, errs = rewriteLogicalOperand(g, stack, t.Lhs, t.ExplicitLhs, errAssignInOrOperand, errs, strict) + t.Rhs, errs = rewriteLogicalOperand(g, stack, t.Rhs, t.ExplicitRhs, errAssignInOrOperand, errs, strict) + } + + if len(errs) > numErrsBefore { + return e, errs } return rewriteDeclaredVarsInExpr(g, stack, e, errs, strict) } +func rewriteLogicalOperand(g *localVarGenerator, stack *localDeclaredVars, body Body, explicit bool, errMsg string, errs Errors, strict bool) (Body, Errors) { + if explicit { + return rewriteLogicalOperandBody(g, stack, body, errs, strict) + } + + // Only explicit operand bodies are allowed to declare vars. + return body, rewriteDeclaredVarsInImplicitBody(g, stack, body, errMsg, errs, strict) +} + func rewriteLogicalOperandBody(g *localVarGenerator, stack *localDeclaredVars, body Body, errs Errors, strict bool) (Body, Errors) { stack.Push() defer stack.Pop() @@ -6907,6 +6997,24 @@ func rewriteLogicalOperandBody(g *localVarGenerator, stack *localDeclaredVars, b return rewriteDeclaredVarsInBody(g, stack, used, body, errs, strict) } +// rewriteDeclaredVarsInImplicitBody rejects assignments made directly in an implicit +// and/or operand or not body. These contribute no bindings to the enclosing body, +// so the assignment is dead code. Only assignments need rejecting, as the parser +// doesn't allow some/every in an implicit body. +func rewriteDeclaredVarsInImplicitBody(g *localVarGenerator, stack *localDeclaredVars, body Body, errMsg string, errs Errors, strict bool) Errors { + for i := range body { + switch { + case body[i].IsAssignment(): + errs = append(errs, newErrorString(CompileErr, body[i].Loc(), errMsg)) + case body[i].IsNot(): + body[i], errs = rewriteNotStatement(g, stack, body[i], errs, strict) + case body[i].IsAnd(), body[i].IsOr(): + body[i], errs = rewriteLogicalStatement(g, stack, body[i], errs, strict) + } + } + return errs +} + func rewriteDeclaredVarsInExpr(g *localVarGenerator, stack *localDeclaredVars, expr *Expr, errs Errors, strict bool) (*Expr, Errors) { vis := NewGenericVisitor(func(x any) bool { var stop bool @@ -6926,7 +7034,7 @@ func rewriteDeclaredVarsInExpr(g *localVarGenerator, stack *localDeclaredVars, e func rewriteDeclaredAssignment(g *localVarGenerator, stack *localDeclaredVars, expr *Expr, errs Errors, strict bool) (*Expr, Errors) { if expr.Negated { - errs = append(errs, NewError(CompileErr, expr.Location, "cannot assign vars inside negated expression")) + errs = append(errs, newErrorString(CompileErr, expr.Location, errAssignInNegated)) return expr, errs } @@ -7324,7 +7432,7 @@ func isBuiltinRefOrVar(bs map[string]*Builtin, unsafeBuiltinsMap map[string]stru return false, nil } -func safetyErrorSlice(unsafe unsafeVars, rewritten map[Var]Var) (result Errors) { +func safetyErrorSlice(unsafe unsafeVars, rewritten map[Var]Var, scope string) (result Errors) { if len(unsafe) == 0 { return } @@ -7337,10 +7445,10 @@ func safetyErrorSlice(unsafe unsafeVars, rewritten map[Var]Var) (result Errors) if !v.IsGenerated() { if _, ok := allFutureKeywords[string(v)]; ok { result = append(result, NewError(UnsafeVarErr, pair.Loc, - "var %[1]v is unsafe (hint: `import future.keywords.%[1]v` to import a future keyword)", v)) + "var %[1]v is unsafe%[2]v (hint: `import future.keywords.%[1]v` to import a future keyword)", v, scope)) continue } - result = append(result, NewError(UnsafeVarErr, pair.Loc, "var %v is unsafe", v)) + result = append(result, NewError(UnsafeVarErr, pair.Loc, "var %v is unsafe%v", v, scope)) } } @@ -7366,13 +7474,73 @@ func safetyErrorSlice(unsafe unsafeVars, rewritten map[Var]Var) (result Errors) } } if len(seen) > before { - result = append(result, NewError(UnsafeVarErr, expr.Expr.Location, "expression is unsafe")) + result = append(result, NewError(UnsafeVarErr, expr.Expr.Location, "expression is unsafe%v", scope)) } } return } +// ruleScopes resolves the "in rule ..." label appended to safety errors for the +// rules of one module, which is only added where a line holds rules of more than +// one name and the location alone is ambiguous. Its index of those lines is built +// on first use, once per module rather than once per error, as the safety stages +// keep reporting errors after the error limit is reached. +type ruleScopes struct { + module *Module + rows map[int]struct{} + built bool +} + +func (s *ruleScopes) scope(rule *Rule) string { + if s == nil || s.module == nil || rule.Location == nil { + return "" + } + + if !s.built { + s.rows = sharedRuleRows(s.module) + s.built = true + } + + if _, ok := s.rows[rule.Location.Row]; !ok { + return "" + } + + // The ground prefix of the head ref is the rule's name: any dynamic part + // (e.g. the key in p[k]) may have been rewritten to a generated local by an + // earlier compiler stage, and isn't needed to identify the rule. + return " in rule " + rule.Head.Ref().GroundPrefix().String() +} + +// sharedRuleRows returns the source rows of module that hold rules of more than +// one name. +func sharedRuleRows(module *Module) map[int]struct{} { + var shared map[int]struct{} + first := map[int]Ref{} + + WalkRules(module, func(rule *Rule) bool { + if rule.Location == nil { + return false + } + + row := rule.Location.Row + name := rule.Head.Ref().GroundPrefix() + + if prev, ok := first[row]; !ok { + first[row] = name + } else if !prev.Equal(name) { + if shared == nil { + shared = map[int]struct{}{} + } + shared[row] = struct{}{} + } + + return false + }) + + return shared +} + func checkUnsafeBuiltins(unsafeBuiltinsMap map[string]struct{}, node any) Errors { var errs Errors WalkExprs(node, func(x *Expr) bool { @@ -7401,6 +7569,39 @@ func rewriteVarsInRef(vars ...map[Var]Var) varRewriter { } } +// mergeRefSubjects merges src into dst, allocating dst if needed. +func mergeRefSubjects(dst, src map[Var]Value) map[Var]Value { + if len(src) == 0 { + return dst + } + if dst == nil { + dst = make(map[Var]Value, len(src)) + } + maps.Copy(dst, src) + return dst +} + +// rewriteRefErrVars returns a varRewriter for rendering refs in type errors. +// Beyond the var-to-var mappings of rewriteVarsInRef, it substitutes generated +// locals recorded in localVarGenerator.subjects with the original term (so +// errors show [1, 2][i] rather than __local0__[i]). It operates on a copy. +func rewriteRefErrVars(subjects map[Var]Value, vars ...map[Var]Var) varRewriter { + return func(node Ref) Ref { + i, _ := TransformVars(node.Copy(), func(v Var) (Value, error) { + if val, ok := subjects[v]; ok { + return CopyValue(val), nil + } + for _, m := range vars { + if u, ok := m[v]; ok { + return u, nil + } + } + return v, nil + }) + return i.(Ref) + } +} + type ruleRefSet struct { s []ruleRef } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/env.go b/vendor/github.com/open-policy-agent/opa/v1/ast/env.go index c3a5fcef02..ff28b58f4d 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/env.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/env.go @@ -14,7 +14,10 @@ import ( // TypeEnv contains type info for static analysis such as type checking. type TypeEnv struct { - tree *typeTreeNode + tree *typeTreeNode + // vars is the tree the types inferred for variables are stored in, which is + // tree except in the environments created for with modifiers. + vars *typeTreeNode next *TypeEnv newChecker func() *typeChecker } @@ -22,8 +25,10 @@ type TypeEnv struct { // newTypeEnv returns an empty TypeEnv. The constructor is not exported because // type environments should only be created by the type checker. func newTypeEnv(f func() *typeChecker) *TypeEnv { + tree := newTypeTree() return &TypeEnv{ - tree: newTypeTree(), + tree: tree, + vars: tree, newChecker: f, } } @@ -231,9 +236,19 @@ func (env *TypeEnv) wrap() *TypeEnv { cpy := *env cpy.next = env cpy.tree = newTypeTree() + cpy.vars = cpy.tree return &cpy } +// wrapWith returns a TypeEnv for checking a single expression carrying with +// modifiers: unlike wrap, the types it infers for variables are kept in the +// enclosing environment, as those variables outlive the expression. +func (env *TypeEnv) wrapWith() *TypeEnv { + cpy := env.wrap() + cpy.vars = env.vars + return cpy +} + // typeTreeNode is used to store type information in a tree. type typeTreeNode struct { key Value diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/index.go b/vendor/github.com/open-policy-agent/opa/v1/ast/index.go index 3502550798..3c0c89e723 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/index.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/index.go @@ -5,6 +5,7 @@ package ast import ( + "maps" "slices" "strings" "sync" @@ -13,15 +14,8 @@ import ( ) var ( - equalityRef = Equality.Ref() - equalRef = Equal.Ref() - globMatchRef = GlobMatch.Ref() - internalPrintRef = InternalPrint.Ref() - internalTestCaseRef = InternalTestCase.Ref() - internalMemberRef = Member.Ref() - globwildcard = VarTerm("$globwildcard") - skipIndexing = NewSet(NewTerm(internalPrintRef), NewTerm(internalTestCaseRef)) + skipIndexing = NewSet(NewTerm(Interned.Refs.InternalPrint), NewTerm(Interned.Refs.InternalTestCase)) // anyValue is a fake variable we used to put "naked ref" expressions // into the rule index @@ -100,7 +94,7 @@ func (i *baseDocEqIndex) Build(rules []*Rule) bool { if i.onlyGroundRefs { i.onlyGroundRefs = rule.Head.Reference.IsGround() } - if !slices.ContainsFunc(rule.Body, skipIndexingOperator) { + if !bodySkipsIndexing(rule.Body) { clear(values) for i := range rule.Body { indices.Update(rule, rule.Body[i], values) @@ -111,53 +105,24 @@ func (i *baseDocEqIndex) Build(rules []*Rule) bool { } // build trie out of indices. + sorted := indices.Sorted() + for idx := range rules { var prio int WalkRules(rules[idx], func(rule *Rule) bool { if rule.Default { return false } - node := i.root - if indices.Indexed(rule) { - for _, ref := range indices.Sorted() { - var values []*refindex - for _, ri := range indices.rules[rule] { - if ri.Ref.Equal(ref) { - values = append(values, ri) - } - } - if len(values) == 0 { - node = node.Insert(ref, nil, nil) - } else if len(values) == 1 { - node = node.Insert(ref, values[0].Value, values[0].Mapper) - } else { - if slices.ContainsFunc(values, (*refindex).isVar) { - child := node.Insert(ref, anyValue, values[0].Mapper) - for i := range values { - if values[i].Mapper != nil { - node.next.addMapper(values[i].Mapper) - } - } - node = child - } else { - // When a rule has multiple scalar values (e.g., internal.member_2 with a set), - // each value should have its own child node, and the rule is appended to each. - // This creates separate paths for each value so different rules with overlapping - // values don't interfere with each other. - for _, val := range values { - child := node.Insert(ref, val.Value, val.Mapper) - child.append([...]int{idx, prio}, rule) - } - prio++ - return false - } - } + // Each set of indices the rule can be reached through gets its own + // path. They share a priority, so a lookup arriving at the rule down + // several of them still reports it once (see trieTraversalResult.Add). + if len(indices.disjunctions[rule]) == 0 { + i.insertPath(sorted, indices.rules[rule], [...]int{idx, prio}, rule) + } else { + for _, path := range indices.paths(rule) { + i.insertPath(sorted, path, [...]int{idx, prio}, rule) } } - // Insert rule into trie with (insertion order, priority order) - // tuple. Retaining the insertion order allows us to return rules - // in the order they were passed to this function. - node.append([...]int{idx, prio}, rule) prio++ return false }) @@ -165,6 +130,51 @@ func (i *baseDocEqIndex) Build(rules []*Rule) bool { return true } +func (i *baseDocEqIndex) insertPath(sorted []Ref, path []*refindex, prio [2]int, rule *Rule) { + node := i.root + + if len(path) > 0 { + for _, ref := range sorted { + var values []*refindex + for _, ri := range path { + if ri.Ref.Equal(ref) { + values = append(values, ri) + } + } + if len(values) == 0 { + node = node.Insert(ref, nil, nil) + } else if len(values) == 1 { + node = node.Insert(ref, values[0].Value, values[0].Mapper) + } else { + if slices.ContainsFunc(values, (*refindex).isVar) { + child := node.Insert(ref, anyValue, values[0].Mapper) + for i := range values { + if values[i].Mapper != nil { + node.next.addMapper(values[i].Mapper) + } + } + node = child + } else { + // When a rule has multiple scalar values (e.g., internal.member_2 with a set), + // each value should have its own child node, and the rule is appended to each. + // This creates separate paths for each value so different rules with overlapping + // values don't interfere with each other. + for _, val := range values { + child := node.Insert(ref, val.Value, val.Mapper) + child.append(prio, rule) + } + return + } + } + } + } + + // Insert rule into trie with (insertion order, priority order) + // tuple. Retaining the insertion order allows us to return rules + // in the order they were passed to this function. + node.append(prio, rule) +} + func (i *baseDocEqIndex) Lookup(resolver ValueResolver) (*IndexResult, error) { tr := ttrPool.Get().(*trieTraversalResult) @@ -205,8 +215,8 @@ func (i *baseDocEqIndex) Lookup(resolver ValueResolver) (*IndexResult, error) { if len(tr.unordered[pos]) == 0 { continue } - slices.SortFunc(tr.unordered[pos], (*ruleNode).prio1Cmp) - nodes := tr.unordered[pos] + + nodes := util.SortedFunc(tr.unordered[pos], (*ruleNode).prio1Cmp) root := nodes[0].rule result.Rules = append(result.Rules, root) @@ -304,13 +314,27 @@ type refindex struct { Mapper *valueMapper } +// alternatives are sets of indices, any one of which is enough to reach a rule. +type alternatives = [][]*refindex + type refindices struct { isVirtual func(Ref) bool rules map[*Rule][]*refindex + // disjunctions holds the alternatives contributed by each `or` in the rule; + // every combination of them is a way to reach it. + disjunctions map[*Rule][]alternatives + // outer holds the enclosing scope's indices when this is the scratch for an + // operand body: resolvable from inside, but not the operand's own. + outer []*refindex frequency *util.HasherMap[Ref, int] sorted []Ref } +// maxIndexPaths caps the ways a single rule may be reached: `or` expressions +// multiply out (`{a or b} and {c or d}` is four), and at some point the trie +// nodes cost more than evaluating the rule. +const maxIndexPaths = 32 + func newrefindices(isVirtual func(Ref) bool) *refindices { return &refindices{ isVirtual: isVirtual, @@ -340,6 +364,15 @@ func (i *refindices) Update(rule *Rule, expr *Expr, values map[Var]Value) { return } + switch terms := expr.Terms.(type) { + case *LogicalAnd: + i.updateLogicalAnd(rule, terms, values) + return + case *LogicalOr: + i.updateLogicalOr(rule, terms, values) + return + } + op := expr.Operator() if op == nil { if ts, ok := expr.Terms.(*Term); ok { @@ -353,12 +386,12 @@ func (i *refindices) Update(rule *Rule, expr *Expr, values map[Var]Value) { } } - equalish := op.Equal(equalityRef) || // unification, no 3-operands version exists + equalish := op.Equal(Interned.Refs.Equality) || // unification, no 3-operands version exists // NOTE(tsandall): if equal() is called with more than two arguments the // output value is being captured in which case the indexer cannot // exclude the rule if the equal() call would return false (because the // false value must still be produced.) - (op.Equal(equalRef) && len(expr.Operands()) == 2) + (op.Equal(Interned.Refs.Equal) && len(expr.Operands()) == 2) a, b := expr.Operand(0), expr.Operand(1) switch { @@ -367,17 +400,141 @@ func (i *refindices) Update(rule *Rule, expr *Expr, values map[Var]Value) { i.updateEq(rule, a.Value, b.Value, values) } - case op.Equal(globMatchRef) && len(expr.Operands()) == 3: + case op.Equal(Interned.Refs.GlobMatch) && len(expr.Operands()) == 3: // NOTE(sr): Same as with equal() above -- 4 operands means the output // of `glob.match` is captured and the rule can thus not be excluded. i.updateGlobMatch(rule, expr) - case op.Equal(internalMemberRef) && len(expr.Operands()) == 2: + case op.Equal(Interned.Refs.Member) && len(expr.Operands()) == 2: // NOTE(sr): Again, 3 operands means captured output (like above). i.updateMember(rule, expr, values) } } +// updateLogicalAnd folds both operands of a conjunction into the rule's +// indices: `lhs and rhs` only succeeds if both operands do, so whatever either +// operand requires of the input, the rule requires. +// +// Each operand is indexed against the indices the rule has so far, not against +// what its sibling contributes: operand bodies are separate scopes, so the same +// var in each is a different var, and resolveVarToRef must not connect them. +func (i *refindices) updateLogicalAnd(rule *Rule, and *LogicalAnd, values map[Var]Value) { + lhs := i.operandAlternatives(rule, and.Lhs, values) + rhs := i.operandAlternatives(rule, and.Rhs, values) + + i.require(rule, lhs) + i.require(rule, rhs) +} + +// require records that the rule is only defined if one of the alternatives +// holds. A lone alternative is unconditional, so its indices join the rule's +// own; several are kept apart for Build to turn into separate paths. +func (i *refindices) require(rule *Rule, alts alternatives) { + switch len(alts) { + case 0: + return + case 1: + for _, ri := range alts[0] { + i.insert(rule, ri) + } + default: + for _, alt := range alts { + for _, ri := range alt { + i.count(ri.Ref) + } + } + if i.disjunctions == nil { + i.disjunctions = map[*Rule][]alternatives{} + } + i.disjunctions[rule] = append(i.disjunctions[rule], alts) + } +} + +// updateLogicalOr records the operands of a disjunction as alternative ways to +// reach the rule, `lhs or rhs` holding if either operand does. An operand +// nothing can be indexed on could be satisfied by any input at all, which +// leaves the disjunction saying nothing about the rule. +func (i *refindices) updateLogicalOr(rule *Rule, or *LogicalOr, values map[Var]Value) { + lhs := i.operandAlternatives(rule, or.Lhs, values) + if len(lhs) == 0 { + return + } + + rhs := i.operandAlternatives(rule, or.Rhs, values) + if len(rhs) == 0 { + return + } + + i.require(rule, slices.Concat(lhs, rhs)) +} + +// operandAlternatives returns the ways the body of an `and`/`or` operand can be +// satisfied; an operand with an `or` of its own has one per branch, and none at +// all means nothing about it could be indexed. It is indexed into a scratch, so +// that what it requires reaches the rule only through require(). +func (i *refindices) operandAlternatives(rule *Rule, body Body, values map[Var]Value) alternatives { + scratch := newrefindices(i.isVirtual) + scratch.outer = append(slices.Clone(i.rules[rule]), i.outer...) + scratch.updateOperand(rule, body, values) + + alts := scratch.paths(rule) + if len(alts) == 1 && len(alts[0]) == 0 { + return nil + } + + for _, alt := range alts { + for pos, ri := range alt { + // The var is scoped to the operand body and must not become + // resolvable from the outside (see resolveVarToRef); that the ref + // has to be defined still holds. + if ri.isVar() { + alt[pos] = &refindex{Ref: ri.Ref, Value: anyValue, Mapper: ri.Mapper} + } + } + } + + return alts +} + +// paths returns every set of indices that can lead to the rule: the ones that +// always hold, combined with one branch from each disjunction. Past +// maxIndexPaths the disjunctions are dropped -- fewer constraints only widen +// what the index admits, so the result stays correct. +func (i *refindices) paths(rule *Rule) alternatives { + unconditional := i.rules[rule] + paths := alternatives{unconditional} + + for _, alts := range i.disjunctions[rule] { + if len(paths)*len(alts) > maxIndexPaths { + return alternatives{unconditional} + } + + combined := make(alternatives, 0, len(paths)*len(alts)) + for _, path := range paths { + for _, alt := range alts { + combined = append(combined, append(slices.Clone(path), alt...)) + } + } + paths = combined + } + + return paths +} + +// updateOperand folds the expressions of an `and`/`or` operand body into the +// rule's indices. An operand body is a closed scope -- bindings made inside it +// reach neither the enclosing body nor the sibling operand (see +// evalLogicalOperand in topdown) -- so its constants are copied in and dropped +// on return. +func (i *refindices) updateOperand(rule *Rule, body Body, values map[Var]Value) { + scoped := make(map[Var]Value, len(values)) + maps.Copy(scoped, values) + + for _, expr := range body { + i.Update(rule, expr, scoped) + } +} + func (i *refindices) isValidIndexRef(ref Ref) bool { // NB(sr): the ordering is intentional, cheapest-first return RootDocumentNames.Contains(ref[0]) && @@ -405,10 +562,6 @@ func (i *refindices) Sorted() []Ref { return i.sorted } -func (i *refindices) Indexed(rule *Rule) bool { - return len(i.rules[rule]) > 0 -} - func (i *refindices) Value(rule *Rule, ref Ref) Value { if index := i.index(rule, ref); index != nil { return index.Value @@ -479,7 +632,7 @@ func (i *refindices) updateGlobMatch(rule *Rule, expr *Expr) { // variable earlier in the query OR a function argument variable. match := expr.Operand(2) if v, ok := match.Value.(Var); ok { - if ref := resolveVarToRef(i.rules[rule], args, v); ref != nil { + if ref := resolveVarToRef(i.resolvable(rule), args, v); ref != nil { i.insert(rule, &refindex{ Ref: ref, Value: arr.Value, @@ -502,7 +655,7 @@ func (i *refindices) updateMember(rule *Rule, expr *Expr, constants map[Var]Valu lhs, rhs := expr.Operand(0), expr.Operand(1) lvar, ok := lhs.Value.(Var) if ok { - lref := resolveVarToRef(i.rules[rule], rule.Head.Args, lvar) + lref := resolveVarToRef(i.resolvable(rule), rule.Head.Args, lvar) if lref != nil { i.updateMemberRefInValue(rule, lref, rhs, constants) // `ref in value` return @@ -562,7 +715,7 @@ func (i *refindices) resolveAndValidateRef(rule *Rule, args []*Term, term *Term) case Ref: ref = v case Var: - ref = resolveVarToRef(i.rules[rule], args, v) + ref = resolveVarToRef(i.resolvable(rule), args, v) default: return nil } @@ -610,9 +763,24 @@ func resolveVarToRef(ri []*refindex, args []*Term, v Var) Ref { return nil } +// resolvable returns the indices a var here can be resolved against: the rule's +// own, plus those of any scope enclosing an operand body. +func (i *refindices) resolvable(rule *Rule) []*refindex { + if len(i.outer) == 0 { + return i.rules[rule] + } + return append(slices.Clone(i.rules[rule]), i.outer...) +} + +// count records that ref took part in indexing a rule, which is what orders the +// trie levels (see Sorted). +func (i *refindices) count(ref Ref) { + count, _ := i.frequency.Get(ref) + i.frequency.Put(ref, count+1) +} + func (i *refindices) insert(rule *Rule, index *refindex) { - count, _ := i.frequency.Get(index.Ref) - i.frequency.Put(index.Ref, count+1) + i.count(index.Ref) _, indexValueIsVar := index.Value.(Var) @@ -1003,7 +1171,7 @@ func (i *refindices) eqOperandsToRefAndValue(rule *Rule, args []*Term, a, b Valu if !ok { return false } - if ref := resolveVarToRef(i.rules[rule], args, v); ref != nil { + if ref := resolveVarToRef(i.resolvable(rule), args, v); ref != nil { i.insert(rule, &refindex{Ref: ref, Value: bval}) return true } @@ -1154,3 +1322,41 @@ func skipIndexingOperator(expr *Expr) bool { op := expr.OperatorTerm() return op != nil && skipIndexing.Contains(op) } + +// bodySkipsIndexing reports whether body contains an expression that must not +// be indexed away, either at the top level or inside a nested body. The nested +// bodies matter: a rule holding a `print` call inside an `and`, `or`, `not` or +// `every` body is still a rule whose side effects are lost if the indexer +// excludes it from evaluation. +func bodySkipsIndexing(body Body) bool { + if slices.ContainsFunc(body, skipIndexingOperator) { + return true + } + for _, expr := range body { + if !exprHasNestedBody(expr) { + continue + } + found := false + WalkBodies(expr, func(b Body) bool { + if !found && slices.ContainsFunc(b, skipIndexingOperator) { + found = true + } + return found + }) + if found { + return true + } + } + return false +} + +// exprHasNestedBody is a cheap pre-check for bodySkipsIndexing: only these +// expression shapes hold a body directly, so only these are worth the cost of +// a full walk. +func exprHasNestedBody(expr *Expr) bool { + switch expr.Terms.(type) { + case *Every, *Not, *LogicalAnd, *LogicalOr: + return true + } + return false +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/index_debug.go b/vendor/github.com/open-policy-agent/opa/v1/ast/index_debug.go index 88d451b175..17d74072ea 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/index_debug.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/index_debug.go @@ -6,8 +6,10 @@ package ast import ( "fmt" - "sort" + "slices" "strings" + + "github.com/open-policy-agent/opa/v1/util" ) func (node *trieNode) mermaid() string { @@ -65,8 +67,8 @@ func (node *trieNode) mermaidFormat(sb *strings.Builder, counter *int, nodeIDs m pairs = append(pairs, scalarPair{key, val}) return false }) - sort.Slice(pairs, func(a, b int) bool { - return pairs[a].key.Compare(pairs[b].key) < 0 + slices.SortFunc(pairs, func(a, b scalarPair) int { + return a.key.Compare(b.key) }) for _, pair := range pairs { var scalarLabel string @@ -159,18 +161,23 @@ func (node *trieNode) format(sb *strings.Builder, depth int) { } if len(node.rules) > 0 { - fmt.Fprintf(sb, " [%d rule(s)]", len(node.rules)) + sb.WriteString(" [") + util.WriteInt(sb, len(node.rules)) + sb.WriteString(" rule(s)]") } if len(node.mappers) > 0 { - fmt.Fprintf(sb, " [%d mapper(s)]", len(node.mappers)) + sb.WriteString(" [") + util.WriteInt(sb, len(node.mappers)) + sb.WriteString(" mapper(s)]") } if node.value != nil { - fmt.Fprintf(sb, " value=%v", node.value) + sb.WriteString(" value=") + sb.WriteString(node.value.String()) } if node.multiple { sb.WriteString(" [multiple]") } - sb.WriteString("\n") + sb.WriteByte('\n') if node.undefined != nil { sb.WriteString(indent) @@ -192,12 +199,12 @@ func (node *trieNode) format(sb *strings.Builder, depth int) { nodes = append(nodes, val) return false }) - sort.Slice(scalars, func(a, b int) bool { - return scalars[a].Compare(scalars[b]) < 0 - }) + slices.SortFunc(scalars, Value.Compare) for i := range scalars { sb.WriteString(indent) - fmt.Fprintf(sb, " %v:\n", scalars[i]) + sb.WriteString(" ") + sb.WriteString(scalars[i].String()) + sb.WriteString(":\n") for j := range nodes { if ValueEqual(scalars[i], scalars[j]) { nodes[j].format(sb, depth+2) diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/interning.go b/vendor/github.com/open-policy-agent/opa/v1/ast/interning.go index 65ffd0b591..7e12367aba 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/interning.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/interning.go @@ -13,12 +13,46 @@ type internable interface { bool | string | int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 } +type interned struct { + Refs *internedRefs +} + +type internedRefs struct { + Equal Ref + Equality Ref + GlobMatch Ref + InternalPrint Ref + InternalTestCase Ref + Member Ref + MemberWithKey Ref + Or Ref + Print Ref + RegoMetadataChain Ref + RegoMetadataRule Ref +} + // NOTE! Great care must be taken **not** to modify the terms returned // from these functions, as they are shared across all callers. // This package is currently considered experimental, and may change // at any time without notice. var ( + Interned = &interned{ + Refs: &internedRefs{ + Equal: Equal.Ref(), + Equality: Equality.Ref(), + GlobMatch: GlobMatch.Ref(), + InternalPrint: InternalPrint.Ref(), + InternalTestCase: InternalTestCase.Ref(), + Member: Member.Ref(), + MemberWithKey: MemberWithKey.Ref(), + Or: Or.Ref(), + Print: Print.Ref(), + RegoMetadataChain: RegoMetadataChain.Ref(), + RegoMetadataRule: RegoMetadataRule.Ref(), + }, + } + InternedNullValue Value = Null{} InternedNullTerm = NewTerm(InternedNullValue) @@ -57,7 +91,12 @@ var ( "internal": Var("internal"), "else": Var("else"), - "i": Var("i"), "j": Var("j"), "k": Var("k"), "v": Var("v"), "x": Var("x"), "y": Var("y"), "z": Var("z"), + "a": Var("a"), "b": Var("b"), "c": Var("c"), + "i": Var("i"), "j": Var("j"), + "k": Var("k"), "v": Var("v"), + "x": Var("x"), "y": Var("y"), "z": Var("z"), + + "allow": Var("allow"), "deny": Var("deny"), } ) diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/location/location.go b/vendor/github.com/open-policy-agent/opa/v1/ast/location/location.go index e08088cff1..9c2213bc5b 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/location/location.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/location/location.go @@ -97,29 +97,31 @@ func (loc *Location) HasFile() bool { return loc != nil && loc.File != "" } -// End returns the (row, col) one past the last rune of loc.Text — an -// exclusive end matching the scanner's offset calculation, so [Start, End) -// covers the text. Columns are counted per rune. Returns (Row, Col) for -// empty text and (0, 0) for a nil receiver. +// End determines the end position of loc. func (loc *Location) End() (row, col int) { if loc == nil { return 0, 0 } + return EndOf(loc.Row, loc.Col, loc.Text) +} - if len(loc.Text) == 0 { - return loc.Row, loc.Col +// EndOf returns the end (row, col) position reached by starting at (row, col) +// and advancing through text. +func EndOf(row, col int, text []byte) (endRow, endCol int) { + if len(text) == 0 { + return row, col } - row = loc.Row + bytes.Count(loc.Text, []byte{'\n'}) - col = loc.Col + endRow = row + bytes.Count(text, []byte{'\n'}) + endCol = col - lastLine := loc.Text - if row != loc.Row { - col = 1 - lastLine = loc.Text[bytes.LastIndex(loc.Text, []byte{'\n'})+1:] + lastLine := text + if endRow != row { + endCol = 1 + lastLine = text[bytes.LastIndex(text, []byte{'\n'})+1:] } - return row, col + utf8.RuneCount(lastLine) + return endRow, endCol + utf8.RuneCount(lastLine) } // Compare returns -1, 0, or 1 to indicate if this loc is less than, equal to, diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/mermaid.go b/vendor/github.com/open-policy-agent/opa/v1/ast/mermaid.go index 5f0911ec11..d16a3ad58b 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/mermaid.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/mermaid.go @@ -280,9 +280,9 @@ func mermaidFormatWith(w *With, b *mermaidBuilder) string { // --- Not --- -func (not *Not) mermaidFormat(b *mermaidBuilder) string { +func (n *Not) mermaidFormat(b *mermaidBuilder) string { id := b.node("stadium", "not") - for i, expr := range not.Body { + for i, expr := range n.Body { exprID := mermaidFormatExpr(expr, b) b.edgeLabeled(id, exprID, strconv.Itoa(i)) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/object.go b/vendor/github.com/open-policy-agent/opa/v1/ast/object.go new file mode 100644 index 0000000000..4255bb8960 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/object.go @@ -0,0 +1,166 @@ +package ast + +import ( + "slices" + "sync" + + "github.com/open-policy-agent/opa/v1/util" +) + +var ObjectBuilderPool = &util.SyncPool[ObjectBuilder]{ + Pool: sync.Pool{ + New: func() any { + return newObjectBuilder(32) + }, + }, +} + +// ObjectBuilder is a builder to help building ast.Object values without having to step through intermediate +// formats and conversions, and with things like resource pooling and interning of keys handled conveniently. +// While ast.Object's support other key types than strings, this builder currently doesn't. +// +// NOTE that this is a helper intended for internal use. While anyone is welcome to use it, the API is not +// considered part of the public API contract, and can change without notice. +type ObjectBuilder struct { + pairs [][2]*Term + keyMapFn func(string) string + valMapFn func(*Term) *Term +} + +func newObjectBuilder(size int) *ObjectBuilder { + return &ObjectBuilder{pairs: make([][2]*Term, 0, size)} +} + +// MapToObject returns an Object mapped from m where an optional key mapper is used to transform +// the keys before they're made to (interned) terms, and a mandatory value mapper to transform +// generic values to terms. +func MapToObject[V any](m map[string]V, keyFn func(string) string, valueFn func(V) *Term) Object { + ob := ObjectBuilderPool.Get().Reset().WithKeyMapper(keyFn) + defer ObjectBuilderPool.Put(ob) + + for k, v := range m { + ob.Item(k, valueFn(v)) + } + return ob.AsObject() +} + +func TryMapToObject[V any](m map[string]V, keyFn func(string) string, valueFn func(V) (*Term, error)) (Object, error) { + ob := ObjectBuilderPool.Get().Reset().WithKeyMapper(keyFn) + defer ObjectBuilderPool.Put(ob) + + for k, v := range m { + t, err := valueFn(v) + if err != nil { + return nil, err + } + ob.Item(k, t) + } + return ob.AsObject(), nil +} + +// WithKeyMapper sets a key mapping function to be used when adding items to the builder. +func (b *ObjectBuilder) WithKeyMapper(fn func(string) string) *ObjectBuilder { + b.keyMapFn = fn + return b +} + +// WithValueMapper sets a value mapping function to be used when adding items to the builder. +func (b *ObjectBuilder) WithValueMapper(fn func(*Term) *Term) *ObjectBuilder { + b.valMapFn = fn + return b +} + +// Grow ensures that the builder has capacity for at least n additional items, and returns the builder. +func (b *ObjectBuilder) Grow(n int) *ObjectBuilder { + if b.pairs == nil { + b.pairs = make([][2]*Term, 0, n) + } else if cap(b.pairs)-len(b.pairs) < n { + b.pairs = slices.Grow(b.pairs, n) + } + return b +} + +// Item adds a key-value pair to the builder, where the key is a string (interned as term) and the value as term. +func (b *ObjectBuilder) Item(key string, value *Term) *ObjectBuilder { + k, v := b.mapKey(key), b.mapValue(value) + if k == nil || v == nil { + return b + } + b.pairs = append(b.pairs, [2]*Term{k, v}) + return b +} + +// AsObject builds and returns an ast.Object. +func (b *ObjectBuilder) AsObject() Object { + return NewObject(b.pairs...) +} + +// AsTerm builds and returns an ast.Object contained in an ast.Term for convenience. +func (b *ObjectBuilder) AsTerm() *Term { + return NewTerm(b.AsObject()) +} + +// Reset resets the builder to an empty state, but keeps the underlying slice for reuse. +func (b *ObjectBuilder) Reset() *ObjectBuilder { + b.pairs = b.pairs[:0] + b.keyMapFn = nil + b.valMapFn = nil + return b +} + +func (b *ObjectBuilder) mapKey(key string) *Term { + if b.keyMapFn != nil { + key = b.keyMapFn(key) + } + return InternedTerm(key) +} + +func (b *ObjectBuilder) mapValue(value *Term) *Term { + if b.valMapFn != nil { + value = b.valMapFn(value) + } + return value +} + +// InterfaceToTermMapper works similarly to [InterfaceToValue], but returns a term instead of a value, +// and tries to find an interned version of the term if possible. +func InterfaceToTermMapper(x any) (*Term, error) { + switch v := x.(type) { + case *Term: + return v, nil + case Value: + // TODO: Find interned term from value + return NewTerm(v), nil + case bool: + return InternedTerm(v), nil + case string: + return InternedTerm(v), nil + case int: + return InternedTerm(v), nil + case int8: + return InternedTerm(v), nil + case int16: + return InternedTerm(v), nil + case int32: + return InternedTerm(v), nil + case int64: + return InternedTerm(v), nil + case uint: + return InternedTerm(v), nil + case uint8: + return InternedTerm(v), nil + case uint16: + return InternedTerm(v), nil + case uint32: + return InternedTerm(v), nil + case uint64: + return InternedTerm(v), nil + default: + x, err := InterfaceToValue(v) + if err != nil { + return nil, err + } + return NewTerm(x), nil // TODO: InternedTerm from value + } + +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/parser.go b/vendor/github.com/open-policy-agent/opa/v1/ast/parser.go index 3ecf654a54..b1d95af6ed 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/parser.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/parser.go @@ -15,7 +15,6 @@ import ( "net/url" "regexp" "slices" - "sort" "strconv" "strings" "unicode/utf8" @@ -29,36 +28,42 @@ import ( "github.com/open-policy-agent/opa/v1/util" ) -// DefaultMaxParsingRecursionDepth is the default maximum recursion -// depth for the parser -const DefaultMaxParsingRecursionDepth = 100000 - -// ErrMaxParsingRecursionDepthExceeded is returned when the parser -// recursion exceeds the maximum allowed depth -var ErrMaxParsingRecursionDepthExceeded = errors.New("max parsing recursion depth exceeded") - -var RegoV1CompatibleRef = Ref{VarTerm("rego"), InternedTerm("v1")} - // RegoVersion defines the Rego syntax requirements for a module. -type RegoVersion int +type RegoVersion uint8 -const DefaultRegoVersion = RegoV1 +const ( + // DefaultRegoVersion is the default Rego version for this OPA version. + DefaultRegoVersion = RegoV1 + + // DefaultMaxParsingRecursionDepth is the default maximum recursion depth for the parser + DefaultMaxParsingRecursionDepth = 100000 +) const ( + // RegoUndefined represents a Rego version unknown to OPA, like for a policy that has + // yet to be parsed and a no version information has been provided by other means. RegoUndefined RegoVersion = iota - // RegoV0 is the default, original Rego syntax. + // RegoV0 is the original Rego syntax, which was used by default in OPA < 1.0. RegoV0 - // RegoV0CompatV1 requires modules to comply with both the RegoV0 and RegoV1 syntax (as when 'rego.v1' is imported in a module). - // Shortly, RegoV1 compatibility is required, but 'rego.v1' or 'future.keywords' must also be imported. + // RegoV0CompatV1 requires modules to comply with both the RegoV0 and RegoV1 + // syntax (requiring Rego v1 imports in a module to use v1 keywords). + // For more information, see https://www.openpolicyagent.org/docs/v0-compatibility RegoV0CompatV1 - // RegoV1 is the Rego syntax enforced by OPA 1.0; e.g.: - // future.keywords part of default keyword set, and don't require imports; - // 'if' and 'contains' required in rule heads; - // (some) strict checks on by default. + // RegoV1 is the Rego syntax enforced by OPA 1.0 and later versions, including the following changes: + // - Keywords `in`, `every`, `ìf` and `contains` now part of the default set, and don't require explicit import + // - Using 'if' and 'contains' now required in rule heads + // - Most compiler checks previously enabled in "strict mode" now enabled by default + // For more information, see https://www.openpolicyagent.org/docs/v0-upgrade RegoV1 ) var ( + // ErrMaxParsingRecursionDepthExceeded is returned when the parser + // recursion exceeds the maximum allowed depth + ErrMaxParsingRecursionDepthExceeded = errors.New("max parsing recursion depth exceeded") + + RegoV1CompatibleRef = Ref{VarTerm("rego"), InternedTerm("v1")} + // this is the name to use for instantiating an empty set, e.g., `set()`. setConstructor = RefTerm(VarTerm("set")) @@ -66,13 +71,6 @@ var ( Var("$0"), Var("$1"), Var("$2"), Var("$3"), Var("$4"), Var("$5"), Var("$6"), Var("$7"), Var("$8"), Var("$9"), Var("$10"), } - - // use static references to avoid allocations, and - // copy them to the call term only when needed - memberWithKeyRef = MemberWithKey.Ref() - memberRef = Member.Ref() - - newlineBytes = []byte{'\n'} metadataBytes = []byte("METADATA") metadataParserPool = util.NewSyncPool[metadataParser]() ) @@ -342,7 +340,6 @@ func (p *Parser) presentParser() (*Parser, map[string]tokens.Token) { // comments as they are found. Any errors encountered while // parsing will be accumulated and returned as a list of Errors. func (p *Parser) Parse() ([]Statement, []*Comment, Errors) { - if p.po.Capabilities == nil { p.po.Capabilities = CapabilitiesForThisVersion(CapabilitiesRegoVersion(p.po.RegoVersion)) } @@ -352,11 +349,7 @@ func (p *Parser) Parse() ([]Statement, []*Comment, Errors) { if p.po.EffectiveRegoVersion() == RegoV1 { if !p.po.Capabilities.ContainsFeature(FeatureRegoV1) { return nil, nil, Errors{ - &Error{ - Code: ParseErr, - Message: "illegal capabilities: rego_v1 feature required for parsing v1 Rego", - Location: nil, - }, + &Error{Code: ParseErr, Message: "illegal capabilities: rego_v1 feature required for parsing v1 Rego"}, } } @@ -370,11 +363,7 @@ func (p *Parser) Parse() ([]Statement, []*Comment, Errors) { // For sake of error reporting, we still need to check that keywords in capabilities are known in v0 if _, ok := futureKeywordsV0[kw]; !ok { return nil, nil, Errors{ - &Error{ - Code: ParseErr, - Message: fmt.Sprintf("illegal capabilities: unknown keyword: %v", kw), - Location: nil, - }, + &Error{Code: ParseErr, Message: "illegal capabilities: unknown keyword: " + kw}, } } } @@ -383,13 +372,7 @@ func (p *Parser) Parse() ([]Statement, []*Comment, Errors) { // Check that explicitly requested future keywords are known. for _, kw := range p.po.FutureKeywords { if _, ok := allowedFutureKeywords[kw]; !ok { - return nil, nil, Errors{ - &Error{ - Code: ParseErr, - Message: fmt.Sprintf("unknown future keyword: %v", kw), - Location: nil, - }, - } + return nil, nil, Errors{&Error{Code: ParseErr, Message: "unknown future keyword: " + kw}} } } } else { @@ -397,13 +380,7 @@ func (p *Parser) Parse() ([]Statement, []*Comment, Errors) { var ok bool allowedFutureKeywords[kw], ok = allFutureKeywords[kw] if !ok { - return nil, nil, Errors{ - &Error{ - Code: ParseErr, - Message: fmt.Sprintf("illegal capabilities: unknown keyword: %v", kw), - Location: nil, - }, - } + return nil, nil, Errors{&Error{Code: ParseErr, Message: "illegal capabilities: unknown keyword: " + kw}} } } @@ -413,48 +390,28 @@ func (p *Parser) Parse() ([]Statement, []*Comment, Errors) { } } - var err error - p.s.s, err = scanner.New(p.r) - if err != nil { - return nil, nil, Errors{ - &Error{ - Code: ParseErr, - Message: err.Error(), - Location: nil, - }, - } - } - - selected := map[string]tokens.Token{} + var selected map[string]tokens.Token if p.po.AllFutureKeywords { + selected = make(map[string]tokens.Token, len(allowedFutureKeywords)) maps.Copy(selected, allowedFutureKeywords) } else { if p.po.EffectiveRegoVersion() == RegoV1 { + selected = make(map[string]tokens.Token, len(futureKeywordsV0)+len(p.po.FutureKeywords)) for kw := range futureKeywordsV0 { tok, ok := allowedFutureKeywords[kw] if !ok { - return nil, nil, Errors{ - &Error{ - Code: ParseErr, - Message: fmt.Sprintf("unknown future keyword: %v", kw), - Location: nil, - }, - } + return nil, nil, Errors{&Error{Code: ParseErr, Message: "unknown future keyword: " + kw}} } selected[kw] = tok } + } else { + selected = make(map[string]tokens.Token, len(p.po.FutureKeywords)) } for _, kw := range p.po.FutureKeywords { tok, ok := allowedFutureKeywords[kw] if !ok { - return nil, nil, Errors{ - &Error{ - Code: ParseErr, - Message: fmt.Sprintf("unknown future keyword: %v", kw), - Location: nil, - }, - } + return nil, nil, Errors{&Error{Code: ParseErr, Message: "unknown future keyword: " + kw}} } selected[kw] = tok } @@ -464,12 +421,13 @@ func (p *Parser) Parse() ([]Statement, []*Comment, Errors) { p.notBodies = true } - p.s.s = p.s.s.WithKeywords(selected) + var err error + if p.s.s, err = scanner.New(p.r); err != nil { + return nil, nil, Errors{&Error{Code: ParseErr, Message: err.Error()}} + } - if p.po.EffectiveRegoVersion() == RegoV1 { - for kw, tok := range futureKeywordsV0 { - p.s.s.AddKeyword(kw, tok) - } + for name, token := range selected { + p.s.s.AddKeyword(name, token) } // read the first token to initialize the parser @@ -484,38 +442,36 @@ func (p *Parser) Parse() ([]Statement, []*Comment, Errors) { // next type of statement. If a statement can be parsed, continue from that // point trying to parse packages, imports, etc. in the same order. for p.s.tok != tokens.EOF { - s := p.save() - - if pkg := p.parsePackage(); pkg != nil { - stmts = append(stmts, pkg) - continue - } else if len(p.s.errors) > 0 { - break - } - - p.restore(s) - s = p.save() - - if imp := p.parseImport(); imp != nil { - if RegoRootDocument.Equal(imp.Path.Value.(Ref)[0]) { - p.regoV1Import(imp) + var s *state + if p.s.tok == tokens.Package { + s = p.save() + if pkg := p.parsePackage(); pkg != nil { + stmts = append(stmts, pkg) + continue + } else if len(p.s.errors) > 0 { + break } + p.restore(s) + } - if FutureRootDocument.Equal(imp.Path.Value.(Ref)[0]) { - p.futureImport(imp, allowedFutureKeywords) + if p.s.tok == tokens.Import { + s = p.save() + if imp := p.parseImport(); imp != nil { + if RegoRootDocument.Equal(imp.Path.Value.(Ref)[0]) { + p.regoV1Import(imp) + } else if FutureRootDocument.Equal(imp.Path.Value.(Ref)[0]) { + p.futureImport(imp, allowedFutureKeywords) + } + stmts = append(stmts, imp) + continue + } else if len(p.s.errors) > 0 { + break } - - stmts = append(stmts, imp) - continue - } else if len(p.s.errors) > 0 { - break + p.restore(s) } - p.restore(s) - if !p.po.SkipRules { s = p.save() - if rules := p.parseRules(); rules != nil { for i := range rules { stmts = append(stmts, rules[i]) @@ -524,7 +480,6 @@ func (p *Parser) Parse() ([]Statement, []*Comment, Errors) { } else if len(p.s.errors) > 0 { break } - p.restore(s) } @@ -558,7 +513,7 @@ func (p *Parser) parseAnnotations(stmts []Statement) []Statement { } func parseAnnotations(comments []*Comment) (stmts []*Annotations, errs Errors) { - numBlocks := CountFunc(comments, IsMetadataComment) + numBlocks := util.Count(IsMetadataComment, comments...) if numBlocks == 0 { return nil, nil } @@ -811,6 +766,26 @@ func scanAheadRef(p *Parser) bool { return false } +// scanAheadLogicalCall rewrites an `and`/`or` keyword token to tokens.Ident when +// it's immediately followed by `(`. Only valid where a term is expected: there, +// the operator reading is impossible, so it must be a function (`&`/`|` set built-ins). +// Operator position is decided before any term is parsed, which is what keeps `x and (b)` a keyword. +func scanAheadLogicalCall(p *Parser) { + if p.s.tok != tokens.LogicalAnd && p.s.tok != tokens.LogicalOr { + return + } + + s := p.save() + p.scanWS() + tok := p.s.tok + p.restore(s) + + if tok == tokens.LParen { + // This is a call to a function named `and`/`or` + p.s.tok = tokens.Ident + } +} + func (p *Parser) parseRules() []*Rule { var rule Rule @@ -897,13 +872,18 @@ func (p *Parser) parseRules() []*Rule { rule.Head.keywords = append(rule.Head.keywords, tokens.If) p.scan() s := p.save() + + // Only a set term with a leading '{' is ambiguous with a body; + // e.g.: 'not {...}' and 'set()' parses to a set literal, but have no ambiguous leading '{' + leadingBrace := p.s.tok == tokens.LBrace + if expr := p.parseLiteral(); expr != nil { // NOTE(sr): set literals are never false or undefined, so parsing this as // p if { true } // ^^^^^^^^ set of one element, `true` // isn't valid. isSetLiteral := false - if t, ok := expr.Terms.(*Term); ok { + if t, ok := expr.Terms.(*Term); ok && leadingBrace { _, isSetLiteral = t.Value.(Set) } // expr.Term is []*Term or Every @@ -913,6 +893,12 @@ func (p *Parser) parseRules() []*Rule { } } + if !leadingBrace { + // Without a leading '{' there is no '{ BODY }' rule body to fall back to, + // so the literal's own error is the useful one; restoring would drop it. + return nil + } + // parsing as literal didn't work out, expect '{ BODY }' p.restore(s) fallthrough @@ -1178,13 +1164,13 @@ func (p *Parser) parseBody(end tokens.Token) Body { } func (p *Parser) parseQuery(requireSemi bool, end tokens.Token) Body { - body := Body{} - if p.s.tok == end { p.error(p.s.Loc(), "found empty body") return nil } + body := Body{} + for { expr := p.parseLiteral() if expr == nil { @@ -1236,12 +1222,19 @@ func (p *Parser) parseLiteral() (expr *Expr) { // binary. Otherwise, restore and fall through to regular handling. if p.s.tok == tokens.LBrace && p.logicalKeywordsActive() { s := p.save() + braceOffset := p.s.loc.Offset bodyLoc := p.s.Loc() p.scan() body := p.parseBody(tokens.RBrace) if body != nil { p.scan() // consume `}` if p.s.tok == tokens.LogicalAnd || p.s.tok == tokens.LogicalOr { + // Only now are the braces known to be an operand rather than a rule body. + if isAmbiguousUnionBody(body) { + p.errorAmbiguousUnionBody(bodyLoc, braceOffset, body, "") + return nil + } + outer := p.parseLogicalOrChain(body, true, bodyLoc) if outer == nil { return nil @@ -1257,7 +1250,7 @@ func (p *Parser) parseLiteral() (expr *Expr) { // parens hold or precede an and/or; otherwise (`({})`, `({a})`, `(a == b)`) it // restores and we fall through so parseExpr handles the term. if p.s.tok == tokens.LParen && p.logicalKeywordsActive() { - if body, explicit, loc, committed := p.parseLogicalGroup(false); committed { + if body, explicit, loc, committed := p.parseLogicalGroup(false, ""); committed { if body == nil { return nil } @@ -1291,7 +1284,9 @@ func (p *Parser) parseLiteral() (expr *Expr) { if nb == nil { return nil } - return p.attachWith(nb) + // A not-body is a complete operand, so it may lead an and/or chain: + // `not { x } and y`. + return p.foldLogicalTail(NewBody(nb), false, nb.Location) } switch p.s.tok { @@ -1325,14 +1320,12 @@ func (p *Parser) isAllowedRefKeywordStr(s string) bool { } func (p *Parser) parseLiteralExpr(negated bool, notLoc *Location) *Expr { - startOffset := p.s.loc.Offset - startLoc := p.s.Loc() s := p.save() // Negated parenthesized group: `not (a or b)`. The parens are an operand of // `not`, so any `{...}` inside is a body. - if negated && p.notBodies && p.s.tok == tokens.LParen && p.logicalKeywordsActive() { - if body, explicit, _, committed := p.parseLogicalGroup(true); committed { + if negated && p.notBodies && p.s.tok == tokens.LParen { + if body, explicit, _, committed := p.parseLogicalGroup(true, "not "); committed { if body == nil { return nil } @@ -1395,10 +1388,22 @@ func (p *Parser) parseLiteralExpr(negated bool, notLoc *Location) *Expr { } if expr.Location == nil { - startLoc.Text = p.s.Text(startOffset, p.s.lastEnd) + startLoc := s.Loc() + startLoc.Text = p.s.Text(startLoc.Offset, p.s.lastEnd) expr.SetLoc(startLoc) } + if notLoc == nil && bytes.HasPrefix(expr.Location.Text, []byte("{")) { + // `{}` on its own is an empty body + if isEmptyObjectTerm(expr) { + p.error(expr.Location, "found empty body") + return nil + } + + p.errorBraceLedOperand(expr.Location, expr.Location.Text, p.s.tok.String()) + return nil + } + outer := p.parseLogicalOrChain(NewBody(expr), false, expr.Location) if outer == nil { return nil @@ -1567,14 +1572,44 @@ func (p *Parser) parseSome() *Expr { } func (p *Parser) parseNotBody(notLoc *Location) *Expr { + braceOffset := p.s.loc.Offset + braceLoc := p.s.Loc() + s := p.save() p.scan() // consume `{` + // `not {}` is an empty body, which parseBody reports precisely; only non-empty + // braces are worth re-reading as a value. + empty := p.s.tok == tokens.RBrace + body := p.parseBody(tokens.RBrace) if body == nil { + if empty { + return nil + } + + // The braces may hold a value rather than a body. If so, report the + // contract and its escapes; if not, keep the body error. + failed := p.save() + p.restore(s) + + // The operand can extend past the braces (`{1, 2} & input.s == set()`), + // and parens group rather than delimit, so it is the whole operand that has to be wrapped. + if term := p.parseTermInfixCall(); term != nil { + p.errorOperandBraceNeedsBody(braceLoc, p.s.Text(braceOffset, p.s.lastEnd), term, "not ") + return nil + } + + p.restore(failed) + return nil } p.scan() // consume `}` + if isAmbiguousUnionBody(body) { + p.errorAmbiguousUnionBody(braceLoc, braceOffset, body, "not ") + return nil + } + // Extend the location to also include the 'not ' prefix spanned := p.extendLoc(notLoc) not := &Not{Body: body, ExplicitBody: true, Location: spanned} @@ -1616,7 +1651,7 @@ func (p *Parser) parseLogicalOrChain(lhsBody Body, lhsExplicit bool, lhsLoc *Loc for p.s.tok == tokens.LogicalOr { p.scan() - rhsBody, rhsExplicit, rhsLoc := p.parseLogicalOperand() + rhsBody, rhsExplicit, rhsLoc := p.parseLogicalOperand("or") if rhsBody == nil { return nil } @@ -1631,6 +1666,8 @@ func (p *Parser) parseLogicalOrChain(lhsBody Body, lhsExplicit bool, lhsLoc *Loc rhsExplicit = false } + p.checkVoidCallOperands(lhsBody, rhsBody, "or") + exprLoc := p.extendLoc(lhsLoc) node := &LogicalOr{ Lhs: lhsBody, @@ -1663,11 +1700,13 @@ func (p *Parser) parseLogicalAndChain(lhsBody Body, lhsExplicit bool, lhsLoc *Lo for p.s.tok == tokens.LogicalAnd { p.scan() - rhsBody, rhsExplicit, _ := p.parseLogicalOperand() + rhsBody, rhsExplicit, _ := p.parseLogicalOperand("and") if rhsBody == nil { return nil } + p.checkVoidCallOperands(lhsBody, rhsBody, "and") + exprLoc := p.extendLoc(lhsLoc) node := &LogicalAnd{ Lhs: lhsBody, @@ -1705,16 +1744,49 @@ func isNegated(p *Parser) bool { return tok != tokens.Dot && tok != tokens.LBrack } -// parseLogicalOperand parses a single operand of an `and`/`or` expression. -func (p *Parser) parseLogicalOperand() (Body, bool, *Location) { +// parseLogicalOperand parses a single operand of an `and`/`or` expression. op is +// the operator the operand belongs to, or "" when the caller is speculating and +// will restore on failure. +func (p *Parser) parseLogicalOperand(op string) (Body, bool, *Location) { if p.s.tok == tokens.LBrace { + braceOffset := p.s.loc.Offset loc := p.s.Loc() + s := p.save() p.scan() + + // `{}` is an empty body, which parseBody reports precisely; only non-empty + // braces are worth re-reading as a value. + empty := p.s.tok == tokens.RBrace + body := p.parseBody(tokens.RBrace) if body == nil { + if empty || op == "" { + return nil, false, nil + } + + // The braces may hold a value rather than a body. + failed := p.save() + p.restore(s) + + // The operand can extend past the braces (`{1, 2} & input.s == set()`), + // and parens group rather than delimit, so it is the whole operand that has to be wrapped. + if term := p.parseTermInfixCall(); term != nil { + p.errorBraceLedOperand(loc, p.s.Text(braceOffset, p.s.lastEnd), op) + return nil, false, nil + } + + p.restore(failed) + return nil, false, nil } p.scan() + + if isAmbiguousUnionBody(body) { + // Report, but hand the body back: if the caller is a paren group that + // restores, the error is rolled back with it. + p.errorAmbiguousUnionBody(loc, braceOffset, body, "") + } + return body, true, loc } @@ -1738,7 +1810,12 @@ func (p *Parser) parseLogicalOperand() (Body, bool, *Location) { // body. If the parens don't hold a logical group parseLogicalGroup restores // state and we fall through so parseExpr can handle `(a == b)` as a term. if p.s.tok == tokens.LParen && p.logicalKeywordsActive() && (!negated || p.notBodies) { - if body, explicit, loc, committed := p.parseLogicalGroup(true); committed { + prefix := "" + if negated { + prefix = "not " + } + + if body, explicit, loc, committed := p.parseLogicalGroup(true, prefix); committed { if body == nil { return nil, false, nil } @@ -1777,6 +1854,138 @@ func (p *Parser) parseLogicalOperand() (Body, bool, *Location) { return NewBody(expr), false, expr.Location } +// isAmbiguousUnionBody reports whether b is a single-expression body holding a +// bare infix `|` set union. Written that way, `{ ... | ... }` cannot be told apart +// from a set comprehension; the call form (`or(x, y)`) and the parenthesized form +// (`(x | y)`) can, and are left alone. +func isAmbiguousUnionBody(b Body) bool { + if len(b) == 0 { + return false + } + + // The first expression decides: `{A | B; C}` also reads as a comprehension with + // head A and body `B; C`, so trailing expressions don't disambiguate anything. + terms, ok := b[0].Terms.([]*Term) + if !ok || !Interned.Refs.Or.Equal(b[0].Operator()) { + return false + } + + // The operator's text is `|` for the infix form and `or` for the call form. + if terms[0].Location == nil || string(terms[0].Location.Text) != "|" { + return false + } + + return b[0].Location == nil || !bytes.HasPrefix(bytes.TrimSpace(b[0].Location.Text), []byte("(")) +} + +// errorOperandBraceNeedsBody reports `{...}` in an operand position holding a value instead of expressions. +func (p *Parser) errorOperandBraceNeedsBody(loc *Location, operand []byte, term *Term, prefix string) { + p.hint(fmt.Sprintf("write `%s(%s)` to negate the value, or `%s{%s}` for a body holding it", + prefix, operand, prefix, operand)) + p.errorf(loc, "`{...}` in an operand position must contain expression(s), got: %s", ValueName(braceLedValue(term))) +} + +// braceLedValue returns the value opened by the leading `{` of t. An infix call +// renders its lhs operand first, so `{1, 2} & s` is brace-led by the set; refs are +// left alone, as `{"a": 1}["a"]` is reported as the ref it is. +func braceLedValue(t *Term) Value { + if call, ok := t.Value.(Call); ok && len(call) > 0 { + if bi, ok := BuiltinMap[call[0].String()]; ok && bi.Infix != "" && len(call) == bi.Decl.Arity()+1 { + return braceLedValue(call[1]) + } + } + + return t.Value +} + +// isEmptyObjectTerm reports whether expr is exactly `{}`. In an operand position +// those braces open a body, so an empty one is an empty body - not the empty +// object the term parser read. +func isEmptyObjectTerm(expr *Expr) bool { + if len(expr.With) > 0 { + return false + } + + t, ok := expr.Terms.(*Term) + if !ok { + return false + } + + obj, ok := t.Value.(Object) + + return ok && obj.Len() == 0 +} + +// errorBraceLedOperand reports an `and`/`or` operand whose leading `{` opens a +// value rather than a body. In an operand position the braces are read as an +// explicit body, so the value form has to be parenthesized, on both sides of the +// operator. +func (p *Parser) errorBraceLedOperand(loc *Location, operand []byte, op string) { + p.hint(fmt.Sprintf("wrap the operand to keep the value: `(%s) %s ...`", operand, op)) + p.errorf(loc, "operand of `%s` cannot begin with `{` unless the braces hold a body", op) +} + +func (p *Parser) checkVoidCallOperands(lhs, rhs Body, op string) { + if name, loc := voidCallOperand(lhs); name != "" { + p.errorVoidCallOperand(loc, name, op) + } + + if name, loc := voidCallOperand(rhs); name != "" { + p.errorVoidCallOperand(loc, name, op) + } +} + +func (p *Parser) errorVoidCallOperand(loc *Location, name, op string) { + p.hint(fmt.Sprintf("`%s` produces no value and always succeeds, so the operand can never fail; move it out of the operand, or add an expression that can fail", name)) + p.errorf(loc, "operand of `%s` cannot consist only of calls to `%s`", op, name) +} + +// voidCallOperand returns the name and location of the first void builtin called by +// an operand whose body does nothing else; negated operands are left alone. +func voidCallOperand(body Body) (string, *Location) { + var name string + var loc *Location + + for _, expr := range body { + if expr.Negated || !expr.IsCall() { + return "", nil + } + + bi, ok := BuiltinMap[expr.Operator().String()] + if !ok || bi.Decl == nil || bi.Decl.Result() != nil { + return "", nil + } + + if name == "" { + name, loc = bi.Name, expr.Location + } + } + + return name, loc +} + +// errorParensCannotWrapBody reports `(...)` holding expressions rather than a value. +func (p *Parser) errorParensCannotWrapBody(loc *Location, braces []byte, prefix string) { + p.hint(fmt.Sprintf("drop the parens to keep the body: `%s%s`", prefix, braces)) + p.error(loc, "`(...)` in an operand position cannot contain a body") +} + +func (p *Parser) errorAmbiguousUnionBody(loc *Location, braceOffset int, body Body, prefix string) { + braces := p.s.Text(braceOffset, p.s.lastEnd) + + // Parenthesizing only the union keeps any trailing expressions of the body. + union := string(braces) + if e := body[0].Location; e != nil { + if rel := e.Offset - braceOffset; rel > 0 && rel+len(e.Text) <= len(braces) { + union = fmt.Sprintf("%s(%s)%s", braces[:rel], e.Text, braces[rel+len(e.Text):]) + } + } + + p.hint(fmt.Sprintf("write `%s(%s)` for the comprehension, or `%s%s` for the set union", + prefix, braces, prefix, union)) + p.error(loc, "ambiguous `{ ... | ... }` operand: read as a body holding a set-union expression, not as a comprehension") +} + // isLogicalBody reports whether b is a single-expression body wrapping a // LogicalAnd/LogicalOr node, i.e. the result of a parenthesized or nested group. func isLogicalBody(b Body) bool { @@ -1820,7 +2029,7 @@ func (p *Parser) expectRParen() bool { // operands starting at the current `(`. // // operandContext reports whether the `(` is already an operand of `and`/`or`/`not`. -func (p *Parser) parseLogicalGroup(operandContext bool) (Body, bool, *Location, bool) { +func (p *Parser) parseLogicalGroup(operandContext bool, prefix string) (Body, bool, *Location, bool) { if !p.enter() { return nil, false, nil, true } @@ -1839,17 +2048,10 @@ func (p *Parser) parseLogicalGroup(operandContext bool) (Body, bool, *Location, return nil, false, nil, false } - // A leading `{` is a body only in an operand context; otherwise it's an - // object/set literal and we backtrack to the term parser. - braceLead := p.s.tok == tokens.LBrace - - lhsBody, lhsExplicit, lhsLoc := p.parseLogicalOperand() + lhsBody, lhsExplicit, lhsLoc := p.parseLogicalOperand("") if lhsBody == nil { - // An empty `{}` operand (e.g. `not ({})`) is a body error. - if operandContext && braceLead { - return nil, false, nil, true - } - + // Parens are not an operand, so a `{...}` that can't be a body is a value: + // restore and let the term parser read it, e.g. `not ({})` is an empty object. p.restore(s) return nil, false, nil, false @@ -1899,16 +2101,22 @@ func (p *Parser) parseLogicalGroup(operandContext bool) (Body, bool, *Location, return nil, false, nil, false case lhsExplicit: - // `({ body })` - if !p.expectRParen() { - return nil, false, nil, true - } + // `({ body })`: parens don't wrap a body. Without a top-level `and`/`or` + // -- handled above -- the braces are a value, so restore and let the term + // parser read them. + braces := p.s.Text(lhsLoc.Offset, p.s.lastEnd) + p.restore(s) - if operandContext || p.s.tok == tokens.LogicalAnd || p.s.tok == tokens.LogicalOr { - return lhsBody, true, p.extendLoc(openLoc), true + probe := p.save() + p.scan() // consume `(` + term := p.parseTerm() + p.restore(probe) + + if term == nil { + p.errorParensCannotWrapBody(openLoc, braces, prefix) + return nil, false, nil, true } - p.restore(s) return nil, false, nil, false case isLogicalBody(lhsBody): @@ -2064,7 +2272,7 @@ func (p *Parser) parseTermIn(lhs *Term, keyVal bool, offset int) *Term { p.scan() if mhs := p.parseTermRelation(nil, offset); mhs != nil { - if op := p.parseTermOpName(memberWithKeyRef, tokens.In); op != nil { + if op := p.parseTermOpName(Interned.Refs.MemberWithKey, tokens.In); op != nil { if rhs := p.parseTermRelation(nil, p.s.loc.Offset); rhs != nil { call := p.setLoc(CallTerm(op, lhs, mhs, rhs), lhs.Location, offset, p.s.lastEnd) switch p.s.tok { @@ -2081,7 +2289,7 @@ func (p *Parser) parseTermIn(lhs *Term, keyVal bool, offset int) *Term { _ = scanAheadRef(p) - if op := p.parseTermOpName(memberRef, tokens.In); op != nil { + if op := p.parseTermOpName(Interned.Refs.Member, tokens.In); op != nil { if rhs := p.parseTermRelation(nil, p.s.loc.Offset); rhs != nil { call := p.setLoc(CallTerm(op, lhs, rhs), lhs.Location, offset, p.s.lastEnd) switch p.s.tok { @@ -2237,6 +2445,10 @@ func (p *Parser) parseTerm() *Term { var term *Term var unaryMinusLoc *Location + + // Check if an `and`/`or` token is actually a function call (`&`/`|` set built-ins). + scanAheadLogicalCall(p) + switch p.s.tok { case tokens.Null: term = NullTerm().SetLocation(p.s.Loc()) @@ -3014,7 +3226,7 @@ func (p *Parser) parseVar() *Term { return NewTerm(p.genwildcard()).SetLocation(p.s.Loc()) } - return VarTerm(p.s.lit).SetLocation(p.s.Loc()) + return NewTerm(InternedVarValue(p.s.lit)).SetLocation(p.s.Loc()) } func (p *Parser) genwildcard() Value { @@ -3049,17 +3261,16 @@ func writeHints(msg *strings.Builder, hints []string) { } func (p *Parser) error(loc *location.Location, reason string) { - msg := reason if len(p.s.hints) > 0 { sb := &strings.Builder{} sb.WriteString(reason) writeHints(sb, p.s.hints) - msg = sb.String() + reason = sb.String() } p.s.errors = append(p.s.errors, &Error{ Code: ParseErr, - Message: msg, + Message: reason, Location: loc, Details: newParserErrorDetail(p.s.s.Bytes(), loc.Offset), }) @@ -3143,11 +3354,10 @@ func (p *Parser) doScan(skipws bool, scanOpts ...scanner.ScanOption) { p.s.loc.Text = p.s.Text(pos.Offset, pos.End) p.s.loc.Tabs = pos.Tabs - for _, err := range errs { - p.error(p.s.Loc(), err.Message) - } - if len(errs) > 0 { + for _, err := range errs { + p.error(p.s.Loc(), err.Message) + } p.s.tok = tokens.Illegal } @@ -3164,19 +3374,30 @@ func (p *Parser) doScan(skipws bool, scanOpts ...scanner.ScanOption) { break } - // For backwards compatibility leave a nil - // Text value if there is no text rather than - // an empty string. - var commentText []byte - if len(p.s.lit) > 1 { - commentText = []byte(p.s.lit[1:]) + var comment *Comment + if len(p.s.loc.Text) != 0 { + // if location has text, use that to avoid allocating for string->[]byte + comment = NewComment(commentFromLocText(p.s.loc.Text[1:])) + } else { + comment = NewComment([]byte(p.s.lit[1:])) } - comment := NewComment(commentText) comment.SetLoc(p.s.Loc()) p.s.comments = append(p.s.comments, comment) } } +func commentFromLocText(commentText []byte) []byte { + l := len(commentText) + if l == 1 && commentText[0] == '\r' { + commentText, l = nil, 0 // special case - remove lone '\r' + } + for l > 1 && commentText[l-1] == '\r' { // trim trailing '\r' until the last char + commentText = commentText[:l-1] + l-- + } + return commentText +} + func (p *Parser) save() *state { cpy := *p.s s := *cpy.s @@ -3448,13 +3669,13 @@ func (b *metadataParser) Parse() (result *Annotations, err error) { result.Location = b.loc // recreate original text of entire metadata block for location text attribute - original := bytes.TrimSuffix(b.buf.Bytes(), newlineBytes) - numLines := bytes.Count(original, newlineBytes) + 1 + original := bytes.TrimSuffix(b.buf.Bytes(), []byte("\n")) + numLines := bytes.Count(original, []byte("\n")) + 1 preAlloc := len("# METADATA\n") + len(original) + numLines*2 // '# ' prefix added per line result.Location.Text = append(make([]byte, 0, preAlloc), "# METADATA\n"...) - for line := range bytes.SplitAfterSeq(original, newlineBytes) { + for line := range bytes.SplitAfterSeq(original, []byte("\n")) { result.Location.Text = append(result.Location.Text, "# "...) result.Location.Text = append(result.Location.Text, line...) } @@ -3672,11 +3893,8 @@ var allFutureKeywords map[string]tokens.Token // experimentalFutureKeywords are future keywords that exist in the parser but are // intentionally hidden from the default capabilities advertisement. // They are only activated when a policy imports them AND the active -// capabilities explicitly list them. -var experimentalFutureKeywords = map[string]struct{}{ - "and": {}, - "or": {}, -} +// capabilities explicitly list them. There are currently none. +var experimentalFutureKeywords = map[string]struct{}{} var allFutureKeywordTokens map[tokens.Token]struct{} @@ -3731,21 +3949,18 @@ func (p *Parser) futureImport(imp *Import, allowedFutureKeywords map[string]toke return } keyword := string(kw) - _, ok = allowedFutureKeywords[keyword] - if !ok { - sort.Strings(kwds) // so the error message is stable - p.errorf(imp.Path.Location, "unexpected keyword, must be one of %v", kwds) + if _, ok = allowedFutureKeywords[keyword]; !ok { + p.errorf(imp.Path.Location, "unexpected keyword, must be one of %v", util.Sorted(kwds)) return } - if keyword == "not" { - p.notBodies = true - } - kwds = []string{keyword} // overwrite } for _, kw := range kwds { + if kw == "not" { + p.notBodies = true + } p.s.s.AddKeyword(kw, allowedFutureKeywords[kw]) } } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/parser_ext.go b/vendor/github.com/open-policy-agent/opa/v1/ast/parser_ext.go index 8b63182c0f..4ee762514a 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/parser_ext.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/parser_ext.go @@ -163,7 +163,6 @@ func MustParseTerm(input string) *Term { // ParseRuleFromBody returns a rule if the body can be interpreted as a rule // definition. Otherwise, an error is returned. func ParseRuleFromBody(module *Module, body Body) (*Rule, error) { - if len(body) != 1 { return nil, errors.New("multiple expressions cannot be used for rule head") } @@ -174,7 +173,6 @@ func ParseRuleFromBody(module *Module, body Body) (*Rule, error) { // ParseRuleFromExpr returns a rule if the expression can be interpreted as a // rule definition. func ParseRuleFromExpr(module *Module, expr *Expr) (*Rule, error) { - if len(expr.With) > 0 { return nil, errors.New("expressions using with keyword cannot be used for rule head") } @@ -224,7 +222,6 @@ func ParseRuleFromExpr(module *Module, expr *Expr) (*Rule, error) { } func parseCompleteRuleFromEq(module *Module, expr *Expr) (rule *Rule, err error) { - // ensure the rule location is set to the expr location // the helper functions called below try to set the location based // on the terms they've been provided but that is not as accurate. @@ -257,15 +254,11 @@ func parseCompleteRuleFromEq(module *Module, expr *Expr) (rule *Rule, err error) // be interpreted as a complete document definition declared with the assignment // operator. func ParseCompleteDocRuleFromAssignmentExpr(module *Module, lhs, rhs *Term) (*Rule, error) { - rule, err := ParseCompleteDocRuleFromEqExpr(module, lhs, rhs) - if err != nil { - return nil, err + if err == nil { + rule.Head.Assign = true } - - rule.Head.Assign = true - - return rule, nil + return rule, err } // ParseCompleteDocRuleFromEqExpr returns a rule if the expression can be @@ -346,10 +339,11 @@ func ParsePartialObjectDocRuleFromEqExpr(module *Module, lhs, rhs *Term) (*Rule, body := NewBody(NewExpr(BooleanTerm(true).SetLocation(rhs.Location)).SetLocation(rhs.Location)) rule := &Rule{ - Location: rhs.Location, - Head: head, - Body: body, - Module: module, + Location: rhs.Location, + Head: head, + Body: body, + Module: module, + generatedBody: true, } return rule, nil @@ -458,7 +452,7 @@ func ParseImports(input string) ([]*Import, error) { if err != nil { return nil, err } - result := []*Import{} + result := make([]*Import, 0, len(stmts)) for _, stmt := range stmts { if imp, ok := stmt.(*Import); ok { result = append(result, imp) @@ -496,12 +490,17 @@ func ParseBody(input string) (Body, error) { // ParseBodyWithOpts returns exactly one body. It does _not_ set SkipRules: true on its own, // but respects whatever ParserOptions it's been given. func ParseBodyWithOpts(input string, popts ParserOptions) (Body, error) { - stmts, _, err := ParseStatementsWithOpts("", input, popts) if err != nil { return nil, err } + if len(stmts) == 1 { + if body, ok := stmts[0].(Body); ok { + return body, nil + } + } + result := Body{} for _, stmt := range stmts { @@ -619,14 +618,7 @@ func ParseRule(input string) (*Rule, error) { // this function expects *exactly* one statement. If multiple // statements are parsed, an error is returned. func ParseStatement(input string) (Statement, error) { - stmts, _, err := ParseStatements("", input) - if err != nil { - return nil, err - } - if len(stmts) != 1 { - return nil, errors.New("expected exactly one statement") - } - return stmts[0], nil + return ParseStatementWithOpts(input, ParserOptions{}) } func ParseStatementWithOpts(input string, popts ParserOptions) (Statement, error) { @@ -640,17 +632,24 @@ func ParseStatementWithOpts(input string, popts ParserOptions) (Statement, error return stmts[0], nil } -// ParseStatements is deprecated. Use ParseStatementWithOpts instead. +// ParseStatements returns a slice of parsed statements. +// +// Deprecated: Use [ParseStatementsWithOpts] instead. func ParseStatements(filename, input string) ([]Statement, []*Comment, error) { return ParseStatementsWithOpts(filename, input, ParserOptions{}) } -// ParseStatementsWithOpts returns a slice of parsed statements. This is the -// default return value from the parser. +// ParseStatementsWithOpts returns a slice of parsed statements. +// This is the default return value from [*Parser.Parse]. func ParseStatementsWithOpts(filename, input string, popts ParserOptions) ([]Statement, []*Comment, error) { + sr := StringReaderPool.Get() + defer StringReaderPool.Put(sr) + + sr.Reset(input) + parser := NewParser(). WithFilename(filename). - WithReader(strings.NewReader(input)). + WithReader(sr). WithProcessAnnotation(popts.ProcessAnnotation). WithFutureKeywords(popts.FutureKeywords...). WithAllFutureKeywords(popts.AllFutureKeywords). diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/performance.go b/vendor/github.com/open-policy-agent/opa/v1/ast/performance.go index 564ee255d1..bea48c701c 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/performance.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/performance.go @@ -5,18 +5,36 @@ package ast import ( "encoding" + "slices" "strings" "sync" ) -var builtinNamesByNumParts = sync.OnceValue(func() map[int][]string { - m := map[int][]string{} +// builtinNameShape packs the two properties of a dotted built-in name that are cheap +// to derive from a ref without allocating — its number of parts and its total length — +// into a single key, which keeps map lookups on the runtime's fast path for 64-bit +// keys. uint64 rather than int so that the shift is well defined on 32-bit platforms. +func builtinNameShape(parts, totalLen int) uint64 { + return uint64(parts)<<32 | uint64(totalLen) +} + +// builtinNamesByShape groups multi-part built-in names by shape, so that +// BuiltinNameFromRef only has to compare against names that could possibly match. +// Bucketing on length as well as part count narrows the candidates from ~100 names +// to a handful, and sorting keeps the scan order deterministic: ranging over +// BuiltinMap yields a fresh random order in every process, which would otherwise +// make the cost of a lookup vary several-fold from one run to the next. +var builtinNamesByShape = sync.OnceValue(func() map[uint64][]string { + m := map[uint64][]string{} for name := range BuiltinMap { - parts := strings.Count(name, ".") + 1 - if parts > 1 { - m[parts] = append(m[parts], name) + if parts := strings.Count(name, ".") + 1; parts > 1 { + shape := builtinNameShape(parts, len(name)) + m[shape] = append(m[shape], name) } } + for _, names := range m { + slices.Sort(names) + } return m }) @@ -50,17 +68,12 @@ func BuiltinNameFromRef(ref Ref) (string, bool) { totalLen += 1 + len(term.Value.(String)) // account for dot } - matched, ok := builtinNamesByNumParts()[reflen] + matched, ok := builtinNamesByShape()[builtinNameShape(reflen, totalLen)] if !ok { return "", false } for _, name := range matched { - // This check saves us a huge amount of work, as only very few built-in - // names will have the exact same length as the ref we are checking. - if len(name) != totalLen { - continue - } // Example: `name` is "io.jwt.decode" (and so is ref) // The first part is varName, which have already been established to be 'io': // io, jwt.decode io == io diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/policy.go b/vendor/github.com/open-policy-agent/opa/v1/ast/policy.go index c5592d99b8..863c9ad599 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/policy.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/policy.go @@ -55,11 +55,15 @@ var RootDocumentNames = NewSet( // All refs to data in the policy engine's storage layer are prefixed with this ref. var DefaultRootRef = Ref{DefaultRootDocument} +var DefaultRootRefTerm = NewTerm(DefaultRootRef) + // InputRootRef is a reference to the root of the input document. // // All refs to query arguments are prefixed with this ref. var InputRootRef = Ref{InputRootDocument} +var InputRootRefTerm = NewTerm(InputRootRef) + // SchemaRootRef is a reference to the root of the schema document. // // All refs to schema documents are prefixed with this ref. Note, the schema @@ -69,10 +73,7 @@ var SchemaRootRef = Ref{SchemaRootDocument} // RootDocumentRefs contains the prefixes of top-level documents that all // non-local references start with. -var RootDocumentRefs = NewSet( - NewTerm(DefaultRootRef), - NewTerm(InputRootRef), -) +var RootDocumentRefs = NewSet(DefaultRootRefTerm, InputRootRefTerm) // SystemDocumentKey is the name of the top-level key that identifies the system // document. @@ -338,10 +339,10 @@ func (mod *Module) Compare(other *Module) int { if cmp := mod.Package.Compare(other.Package); cmp != 0 { return cmp } - if cmp := importsCompare(mod.Imports, other.Imports); cmp != 0 { + if cmp := slices.CompareFunc(mod.Imports, other.Imports, (*Import).Compare); cmp != 0 { return cmp } - if cmp := annotationsCompare(mod.Annotations, other.Annotations); cmp != 0 { + if cmp := slices.CompareFunc(mod.Annotations, other.Annotations, (*Annotations).Compare); cmp != 0 { return cmp } return rulesCompare(mod.Rules, other.Rules) @@ -389,7 +390,7 @@ func (mod *Module) Copy() *Module { // Equal returns true if mod equals other. func (mod *Module) Equal(other *Module) bool { - return mod.Compare(other) == 0 + return mod == other || mod.Compare(other) == 0 } func (mod *Module) String() string { @@ -459,7 +460,7 @@ func (c *Comment) Copy() *Comment { // Unlike other equality checks on AST nodes, comment equality // depends on location. func (c *Comment) Equal(other *Comment) bool { - return c.Location.Equal(other.Location) && bytes.Equal(c.Text, other.Text) + return c == other || (c.Location.Equal(other.Location) && bytes.Equal(c.Text, other.Text)) } // Compare returns an integer indicating whether pkg is less than, equal to, @@ -477,7 +478,7 @@ func (pkg *Package) Copy() *Package { // Equal returns true if pkg is equal to other. func (pkg *Package) Equal(other *Package) bool { - return pkg.Compare(other) == 0 + return pkg == other || pkg.Compare(other) == 0 } // Loc returns the location of the Package in the definition. @@ -548,7 +549,7 @@ func (imp *Import) Copy() *Import { // Equal returns true if imp is equal to other. func (imp *Import) Equal(other *Import) bool { - return imp.Compare(other) == 0 + return imp == other || imp.Compare(other) == 0 } // Loc returns the location of the Import in the definition. @@ -612,7 +613,7 @@ func (rule *Rule) Compare(other *Rule) int { return cmp } - if cmp := annotationsCompare(rule.Annotations, other.Annotations); cmp != 0 { + if cmp := slices.CompareFunc(rule.Annotations, other.Annotations, (*Annotations).Compare); cmp != 0 { return cmp } @@ -640,7 +641,7 @@ func (rule *Rule) Copy() *Rule { // Equal returns true if rule is equal to other. func (rule *Rule) Equal(other *Rule) bool { - return rule.Compare(other) == 0 + return rule == other || rule.Compare(other) == 0 } // Loc returns the location of the Rule in the definition. @@ -851,7 +852,7 @@ func (head *Head) Copy() *Head { // Equal returns true if this head equals other. func (head *Head) Equal(other *Head) bool { - return head.Compare(other) == 0 + return head == other || head.Compare(other) == 0 } func (head *Head) String() string { @@ -965,11 +966,7 @@ func (body Body) Compare(other Body) int { // Copy returns a deep copy of body. func (body Body) Copy() Body { - cpy := make(Body, len(body)) - for i := range body { - cpy[i] = body[i].Copy() - } - return cpy + return util.Map(body, (*Expr).Copy) } // Contains returns true if this body contains the given expression. @@ -979,7 +976,7 @@ func (body Body) Contains(x *Expr) bool { // Equal returns true if this Body is equal to the other Body. func (body Body) Equal(other Body) bool { - return body.Compare(other) == 0 + return slices.EqualFunc(body, other, (*Expr).Equal) } // Hash returns the hash code for the Body. @@ -993,12 +990,7 @@ func (body Body) Hash() int { // IsGround returns true if all of the expressions in the Body are ground. func (body Body) IsGround() bool { - for _, e := range body { - if !e.IsGround() { - return false - } - } - return true + return util.Every(body, (*Expr).IsGround) } // Loc returns the location of the Body in the definition. @@ -1070,7 +1062,7 @@ func (expr *Expr) ComplementNoWith() *Expr { // Equal returns true if this Expr equals the other Expr. func (expr *Expr) Equal(other *Expr) bool { - return expr.Compare(other) == 0 + return expr == other || expr.Compare(other) == 0 } // Compare returns an integer indicating whether expr is less than, equal to, @@ -1086,12 +1078,12 @@ func (expr *Expr) Equal(other *Expr) bool { // Otherwise, the expression terms are compared normally. If both expressions // have the same terms, the modifiers are compared. func (expr *Expr) Compare(other *Expr) int { - if expr == nil { - if other == nil { - return 0 - } + switch { + case expr == other: + return 0 + case expr == nil: return -1 - } else if other == nil { + case other == nil: return 1 } @@ -1187,7 +1179,6 @@ func (expr *Expr) CopyWithoutTerms() *Expr { // Copy returns a deep copy of expr. func (expr *Expr) Copy() *Expr { - cpy := expr.CopyWithoutTerms() switch ts := expr.Terms.(type) { @@ -1308,7 +1299,11 @@ func (expr *Expr) Operator() Ref { if op == nil { return nil } - return op.Value.(Ref) + ref, ok := op.Value.(Ref) + if !ok { + return nil + } + return ref } // OperatorTerm returns the name of the function or built-in this expression @@ -1485,17 +1480,19 @@ func (d *SomeDecl) Hash() int { } func (q *Every) String() string { + b := bytes.NewBufferString("every ") if q.Key != nil { - return fmt.Sprintf("every %s, %s in %s { %s }", - q.Key, - q.Value, - q.Domain, - q.Body) + util.WriteAppender(b, q.Key) + b.WriteString(", ") } - return fmt.Sprintf("every %s in %s { %s }", - q.Value, - q.Domain, - q.Body) + util.WriteAppender(b, q.Value) + b.WriteString(" in ") + util.WriteAppender(b, q.Domain) + b.WriteString(" { ") + util.WriteAppender(b, q.Body) + b.WriteString(" }") + + return b.String() } func (q *Every) Loc() *Location { @@ -1632,7 +1629,7 @@ func logicalOperandNeedsParens(b Body, parentOp string, rhs bool) bool { return true } - switch e.Terms.(type) { + switch t := e.Terms.(type) { case *LogicalOr: // `or` binds looser than `and`: always parenthesize under `and`; under // `or`, parenthesize only the rhs to preserve right-nesting. @@ -1641,6 +1638,8 @@ func logicalOperandNeedsParens(b Body, parentOp string, rhs bool) bool { // `and` binds tighter: no parens under `or`; under `and`, parenthesize // only the rhs to preserve right-nesting. return parentOp == "and" && rhs + case *Term: + return rendersWithLeadingBrace(t.Value) } return false } @@ -1655,10 +1654,33 @@ func notBodyNeedsParens(b Body) bool { return true } - switch e.Terms.(type) { + switch t := e.Terms.(type) { case *LogicalOr, *LogicalAnd: // `not` binds tighter than `and`/`or` return true + case *Not: + // `not not x` doesn't parse: the operand of a `not` must be parenthesized + // for the inner negation to be read back as a body. + return true + case *Term: + return rendersWithLeadingBrace(t.Value) + } + + return false +} + +// rendersWithLeadingBrace reports whether v renders starting with a `{`. Such a +// value needs parens in an operand position, as bare braces there are read as an +// explicit body. +func rendersWithLeadingBrace(v Value) bool { + switch t := v.(type) { + case Set: + // The empty set renders as `set()`. + return t.Len() > 0 + case Object, *SetComprehension, *ObjectComprehension: + return true + case Ref: + return len(t) > 0 && rendersWithLeadingBrace(t[0].Value) } return false @@ -1671,7 +1693,7 @@ func (w *With) String() string { // Equal returns true if this With is equals the other With. func (w *With) Equal(other *With) bool { - return Compare(w, other) == 0 + return w == other || w.Compare(other) == 0 } // Compare returns an integer indicating whether w is less than, equal to, or @@ -1685,10 +1707,10 @@ func (w *With) Compare(other *With) int { } else if other == nil { return 1 } - if cmp := Compare(w.Target, other.Target); cmp != 0 { + if cmp := w.Target.Value.Compare(other.Target.Value); cmp != 0 { return cmp } - return Compare(w.Value, other.Value) + return w.Value.Value.Compare(other.Value.Value) } // Copy returns a deep copy of w. @@ -1835,11 +1857,7 @@ func (rs RuleSet) Merge(other RuleSet) RuleSet { } func (rs RuleSet) String() string { - buf := make([]string, 0, len(rs)) - for _, rule := range rs { - buf = append(buf, rule.String()) - } - return "{" + strings.Join(buf, ", ") + "}" + return "{" + strings.Join(util.Map(rs, (*Rule).String), ", ") + "}" } // Returns true if the equality or assignment expression referred to by expr diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/policy_appenders.go b/vendor/github.com/open-policy-agent/opa/v1/ast/policy_appenders.go index 63b260e0a5..ccc0554ad0 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/policy_appenders.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/policy_appenders.go @@ -7,8 +7,8 @@ import ( "github.com/open-policy-agent/opa/v1/util" ) -func (m *Module) AppendText(buf []byte) ([]byte, error) { - if m == nil { +func (mod *Module) AppendText(buf []byte) ([]byte, error) { + if mod == nil { return append(buf, ""...), nil } @@ -17,7 +17,7 @@ func (m *Module) AppendText(buf []byte) ([]byte, error) { // NOTE(anderseknert): this DOES allocate still, and while that's unfortunate, // we'll be better off dealing with that when we have v2 JSON in the stdlib than // doing manual JSON marshalling (and string length calculations) here. - for _, annotations := range m.Annotations { + for _, annotations := range mod.Annotations { // rule annotations are attached to rules, so only check for package scoped ones here if annotations.Scope == "package" || annotations.Scope == "subpackages" { buf = append(buf, "# METADATA\n# "...) @@ -26,13 +26,13 @@ func (m *Module) AppendText(buf []byte) ([]byte, error) { } } - if buf, err = m.Package.AppendText(buf); err != nil { + if buf, err = mod.Package.AppendText(buf); err != nil { return nil, err } buf = append(buf, '\n') - if len(m.Imports) > 0 { - for _, imp := range m.Imports { + if len(mod.Imports) > 0 { + for _, imp := range mod.Imports { buf = append(buf, '\n') if buf, err = imp.AppendText(buf); err != nil { return nil, err @@ -41,10 +41,10 @@ func (m *Module) AppendText(buf []byte) ([]byte, error) { buf = append(buf, '\n') } - if len(m.Rules) > 0 { - for _, rule := range m.Rules { + if len(mod.Rules) > 0 { + for _, rule := range mod.Rules { buf = append(buf, '\n') - if buf, err = rule.appendWithOpts(toStringOpts{regoVersion: m.regoVersion}, buf); err != nil { + if buf, err = rule.appendWithOpts(toStringOpts{regoVersion: mod.regoVersion}, buf); err != nil { return nil, err } } @@ -86,45 +86,45 @@ func (imp *Import) AppendText(buf []byte) ([]byte, error) { return buf, nil } -func (r *Rule) AppendText(buf []byte) ([]byte, error) { +func (rule *Rule) AppendText(buf []byte) ([]byte, error) { regoVersion := DefaultRegoVersion - if r.Module != nil { - regoVersion = r.Module.RegoVersion() + if rule.Module != nil { + regoVersion = rule.Module.RegoVersion() } - return r.appendWithOpts(toStringOpts{regoVersion: regoVersion}, buf) + return rule.appendWithOpts(toStringOpts{regoVersion: regoVersion}, buf) } -func (r *Rule) appendWithOpts(opts toStringOpts, buf []byte) ([]byte, error) { +func (rule *Rule) appendWithOpts(opts toStringOpts, buf []byte) ([]byte, error) { // See note in [Module.AppendText] regarding annotations. - for _, annotations := range r.Annotations { + for _, annotations := range rule.Annotations { buf = append(buf, "# METADATA\n# "...) buf = append(buf, annotations.String()...) buf = append(buf, '\n') } - if r.Default { + if rule.Default { buf = append(buf, "default "...) } var err error - if buf, err = r.Head.appendWithOpts(opts, buf); err != nil { + if buf, err = rule.Head.appendWithOpts(opts, buf); err != nil { return nil, err } - if !r.Default { + if !rule.Default { switch opts.RegoVersion() { case RegoV1, RegoV0CompatV1: buf = append(buf, " if { "...) default: buf = append(buf, " { "...) } - if buf, err = r.Body.AppendText(buf); err != nil { + if buf, err = rule.Body.AppendText(buf); err != nil { return nil, err } buf = append(buf, " }"...) } - if r.Else != nil { - if buf, err = r.Else.appendElse(opts, buf); err != nil { + if rule.Else != nil { + if buf, err = rule.Else.appendElse(opts, buf); err != nil { return nil, err } } @@ -132,13 +132,13 @@ func (r *Rule) appendWithOpts(opts toStringOpts, buf []byte) ([]byte, error) { return buf, nil } -func (r *Rule) appendElse(opts toStringOpts, buf []byte) ([]byte, error) { +func (rule *Rule) appendElse(opts toStringOpts, buf []byte) ([]byte, error) { buf = append(buf, " else "...) var err error - if r.Head.Value != nil { + if rule.Head.Value != nil { buf = append(buf, "= "...) - if buf, err = r.Head.Value.AppendText(buf); err != nil { + if buf, err = rule.Head.Value.AppendText(buf); err != nil { return nil, err } } @@ -148,13 +148,13 @@ func (r *Rule) appendElse(opts toStringOpts, buf []byte) ([]byte, error) { } else { buf = append(buf, " { "...) } - if buf, err = r.Body.AppendText(buf); err != nil { + if buf, err = rule.Body.AppendText(buf); err != nil { return nil, err } buf = append(buf, " }"...) - if r.Else != nil { - if buf, err = r.Else.appendElse(opts, buf); err != nil { + if rule.Else != nil { + if buf, err = rule.Else.appendElse(opts, buf); err != nil { return nil, err } } @@ -162,52 +162,52 @@ func (r *Rule) appendElse(opts toStringOpts, buf []byte) ([]byte, error) { return buf, nil } -func (h *Head) AppendText(buf []byte) ([]byte, error) { - return h.appendWithOpts(toStringOpts{}, buf) +func (head *Head) AppendText(buf []byte) ([]byte, error) { + return head.appendWithOpts(toStringOpts{}, buf) } -func (h *Head) appendWithOpts(opts toStringOpts, buf []byte) ([]byte, error) { +func (head *Head) appendWithOpts(opts toStringOpts, buf []byte) ([]byte, error) { var err error - if h.Reference == nil { - buf = append(buf, h.Name...) + if head.Reference == nil { + buf = append(buf, head.Name...) } else { - if buf, err = h.Reference.AppendText(buf); err != nil { + if buf, err = head.Reference.AppendText(buf); err != nil { return nil, err } } containsAdded := false switch { - case len(h.Args) != 0: - if buf, err = h.Args.AppendText(buf); err != nil { + case len(head.Args) != 0: + if buf, err = head.Args.AppendText(buf); err != nil { return nil, err } - case len(h.Reference) == 1 && h.Key != nil: + case len(head.Reference) == 1 && head.Key != nil: switch opts.RegoVersion() { case RegoV0: buf = append(buf, '[') - if buf, err = h.Key.AppendText(buf); err != nil { + if buf, err = head.Key.AppendText(buf); err != nil { return nil, err } buf = append(buf, ']') default: - if buf, err = h.Key.AppendText(append(buf, " contains "...)); err != nil { + if buf, err = head.Key.AppendText(append(buf, " contains "...)); err != nil { return nil, err } containsAdded = true } } - if h.Value != nil { - if h.Assign { + if head.Value != nil { + if head.Assign { buf = append(buf, " := "...) } else { buf = append(buf, " = "...) } - if buf, err = h.Value.AppendText(buf); err != nil { + if buf, err = head.Value.AppendText(buf); err != nil { return nil, err } - } else if !containsAdded && h.Name == "" && h.Key != nil { - if buf, err = h.Key.AppendText(append(buf, " contains "...)); err != nil { + } else if !containsAdded && head.Name == "" && head.Key != nil { + if buf, err = head.Key.AppendText(append(buf, " contains "...)); err != nil { return nil, err } } @@ -275,20 +275,20 @@ func (w *With) AppendText(buf []byte) ([]byte, error) { return buf, nil } -func (w *Every) AppendText(buf []byte) ([]byte, error) { +func (q *Every) AppendText(buf []byte) ([]byte, error) { buf = append(buf, "every "...) var err error - if w.Key != nil { - if buf, err = w.Key.AppendText(buf); err != nil { + if q.Key != nil { + if buf, err = q.Key.AppendText(buf); err != nil { return nil, err } buf = append(buf, ", "...) } - if buf, err = w.Value.AppendText(buf); err == nil { + if buf, err = q.Value.AppendText(buf); err == nil { buf = append(buf, " in "...) - if buf, err = w.Domain.AppendText(buf); err == nil { + if buf, err = q.Domain.AppendText(buf); err == nil { buf = append(buf, " { "...) - if buf, err = w.Body.AppendText(buf); err == nil { + if buf, err = q.Body.AppendText(buf); err == nil { buf = append(buf, " }"...) } } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/policy_jsonv2.go b/vendor/github.com/open-policy-agent/opa/v1/ast/policy_jsonv2.go index faf1b55df7..cb2b6fac5c 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/policy_jsonv2.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/policy_jsonv2.go @@ -66,13 +66,13 @@ func (a Args) MarshalJSONTo(e *jsontext.Encoder) error { // Rego source rather than as JSON. Module's own fields are fully described by // their struct tags, so the encoding is left to them, as it is pre-1.27. The // field types provide their own MarshalJSONTo where one is needed. -func (m *Module) MarshalJSONTo(e *jsontext.Encoder) error { +func (mod *Module) MarshalJSONTo(e *jsontext.Encoder) error { // Declare a new type and use a type conversion to avoid recursively calling // Module#MarshalJSONTo. It's the highest precedence marshaller, so there is // nothing below it to fall to, and the new type has no methods of its own. type module Module - return json.MarshalEncode(e, (*module)(m)) + return json.MarshalEncode(e, (*module)(mod)) } func (pkg *Package) MarshalJSONTo(e *jsontext.Encoder) error { @@ -91,57 +91,57 @@ func (pkg *Package) MarshalJSONTo(e *jsontext.Encoder) error { return e.WriteToken(jsontext.EndObject) } -func (i *Import) MarshalJSONTo(e *jsontext.Encoder) error { +func (imp *Import) MarshalJSONTo(e *jsontext.Encoder) error { e.WriteToken(jsontext.BeginObject) - if err := jsonv2.WriteField(e, "path", i.Path); err != nil { + if err := jsonv2.WriteField(e, "path", imp.Path); err != nil { return err } - if astJSON.GetOptions().MarshalOptions.IncludeLocation.Import && i.Location != nil { - if err := jsonv2.WriteField(e, "location", i.Location); err != nil { + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Import && imp.Location != nil { + if err := jsonv2.WriteField(e, "location", imp.Location); err != nil { return err } } - if len(i.Alias) > 0 { + if len(imp.Alias) > 0 { e.WriteToken(jsontext.String("alias")) - e.WriteToken(jsontext.String(string(i.Alias))) + e.WriteToken(jsontext.String(string(imp.Alias))) } return e.WriteToken(jsontext.EndObject) } -func (r *Rule) MarshalJSONTo(e *jsontext.Encoder) error { +func (rule *Rule) MarshalJSONTo(e *jsontext.Encoder) error { e.WriteToken(jsontext.BeginObject) - if r.Default { + if rule.Default { e.WriteToken(jsontext.String("default")) e.WriteToken(jsontext.True) } - if r.Else != nil { - if err := jsonv2.WriteField(e, "else", r.Else); err != nil { + if rule.Else != nil { + if err := jsonv2.WriteField(e, "else", rule.Else); err != nil { return err } } - if err := jsonv2.WriteField(e, "head", r.Head); err != nil { + if err := jsonv2.WriteField(e, "head", rule.Head); err != nil { return err } - if err := jsonv2.WriteField(e, "body", r.Body); err != nil { + if err := jsonv2.WriteField(e, "body", rule.Body); err != nil { return err } - if len(r.Annotations) > 0 { - if err := jsonv2.WriteFieldArray(e, "annotations", r.Annotations); err != nil { + if len(rule.Annotations) > 0 { + if err := jsonv2.WriteFieldArray(e, "annotations", rule.Annotations); err != nil { return err } } - if astJSON.GetOptions().MarshalOptions.IncludeLocation.Rule && r.Location != nil { - if err := jsonv2.WriteField(e, "location", r.Location); err != nil { + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Rule && rule.Location != nil { + if err := jsonv2.WriteField(e, "location", rule.Location); err != nil { return err } } @@ -149,43 +149,43 @@ func (r *Rule) MarshalJSONTo(e *jsontext.Encoder) error { return e.WriteToken(jsontext.EndObject) } -func (h *Head) MarshalJSONTo(e *jsontext.Encoder) error { +func (head *Head) MarshalJSONTo(e *jsontext.Encoder) error { e.WriteToken(jsontext.BeginObject) - if h.Name != "" { + if head.Name != "" { e.WriteToken(jsontext.String("name")) - e.WriteToken(jsontext.String(string(h.Name))) + e.WriteToken(jsontext.String(string(head.Name))) } - if err := jsonv2.WriteField(e, "ref", h.Ref()); err != nil { + if err := jsonv2.WriteField(e, "ref", head.Ref()); err != nil { return err } - if len(h.Args) > 0 { - if err := jsonv2.WriteFieldArray(e, "args", h.Args); err != nil { + if len(head.Args) > 0 { + if err := jsonv2.WriteFieldArray(e, "args", head.Args); err != nil { return err } } - if h.Key != nil { - if err := jsonv2.WriteField(e, "key", h.Key); err != nil { + if head.Key != nil { + if err := jsonv2.WriteField(e, "key", head.Key); err != nil { return err } } - if h.Value != nil { - if err := jsonv2.WriteField(e, "value", h.Value); err != nil { + if head.Value != nil { + if err := jsonv2.WriteField(e, "value", head.Value); err != nil { return err } } - if h.Assign { + if head.Assign { e.WriteToken(jsontext.String("assign")) e.WriteToken(jsontext.True) } - if astJSON.GetOptions().MarshalOptions.IncludeLocation.Head && h.Location != nil { - if err := jsonv2.WriteField(e, "location", h.Location); err != nil { + if astJSON.GetOptions().MarshalOptions.IncludeLocation.Head && head.Location != nil { + if err := jsonv2.WriteField(e, "location", head.Location); err != nil { return err } } @@ -258,8 +258,8 @@ func (q *Every) MarshalJSONTo(e *jsontext.Encoder) error { return e.WriteToken(jsontext.EndObject) } -func (b Body) MarshalJSONTo(e *jsontext.Encoder) error { - return jsonv2.WriteMarshalerToArray(e, b) +func (body Body) MarshalJSONTo(e *jsontext.Encoder) error { + return jsonv2.WriteMarshalerToArray(e, body) } // MarshalJSON returns JSON encoded bytes representing body. @@ -280,46 +280,46 @@ func (expr *Expr) UnmarshalJSON(bs []byte) error { return unmarshalExpr(expr, v) } -func (e *Expr) MarshalJSONTo(enc *jsontext.Encoder) error { +func (expr *Expr) MarshalJSONTo(enc *jsontext.Encoder) error { enc.WriteToken(jsontext.BeginObject) enc.WriteToken(jsontext.String("index")) - enc.WriteToken(jsontext.Int(int64(e.Index))) + enc.WriteToken(jsontext.Int(int64(expr.Index))) includeLocation := astJSON.GetOptions().MarshalOptions.IncludeLocation - if e.Location != nil && includeLocation.Expr { - if err := jsonv2.WriteField(enc, "location", e.Location); err != nil { + if expr.Location != nil && includeLocation.Expr { + if err := jsonv2.WriteField(enc, "location", expr.Location); err != nil { return err } } - if e.Negated { + if expr.Negated { enc.WriteToken(jsontext.String("negated")) enc.WriteToken(jsontext.True) } - if e.Generated { + if expr.Generated { enc.WriteToken(jsontext.String("generated")) enc.WriteToken(jsontext.True) } enc.WriteToken(jsontext.String("terms")) var err error - switch t := e.Terms.(type) { + switch t := expr.Terms.(type) { case []*Term: err = jsonv2.WriteMarshalerToArrayOrNull(enc, t) case json.MarshalerTo: err = t.MarshalJSONTo(enc) default: - return fmt.Errorf("unsupported expr terms type: %T", e.Terms) + return fmt.Errorf("unsupported expr terms type: %T", expr.Terms) } if err != nil { return fmt.Errorf("failed to marshal expr terms: %w", err) } - if len(e.With) > 0 { - if err := jsonv2.WriteFieldArray(enc, "with", e.With); err != nil { + if len(expr.With) > 0 { + if err := jsonv2.WriteFieldArray(enc, "with", expr.With); err != nil { return err } } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/slices.go b/vendor/github.com/open-policy-agent/opa/v1/ast/slices.go deleted file mode 100644 index 5921ec0ca1..0000000000 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/slices.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2026 The OPA Authors. All rights reserved. -// Use of this source code is governed by an Apache2 -// license that can be found in the LICENSE file. - -package ast - -// CountFunc counts the number of items in a slice S that satisfy predicate function f. -func CountFunc[T any, S ~[]T](items S, f func(T) bool) (n int) { - for i := range items { - if f(items[i]) { - n++ - } - } - return n -} diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/string_length.go b/vendor/github.com/open-policy-agent/opa/v1/ast/string_length.go index 09247effa6..b72079ecb4 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/string_length.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/string_length.go @@ -27,17 +27,16 @@ func TermSliceStringLength(terms []*Term, delimLen int) (n int) { return max(n-delimLen, 0) } -func (t *Term) StringLength() int { - if sl, ok := t.Value.(StringLengther); ok { +func (term *Term) StringLength() int { + if sl, ok := term.Value.(StringLengther); ok { return sl.StringLength() } - - panic("expected all ast.Value types to implement StringLenghter interface, got: " + ValueName(t.Value)) + panic("expected all ast.Value types to implement StringLenghter interface, got: " + ValueName(term.Value)) } -func (s String) StringLength() int { +func (str String) StringLength() int { n := 2 // surrounding quotes - bs := util.StringToByteSlice(s) + bs := util.StringToByteSlice(str) for i := 0; i < len(bs); { r, size := utf8.DecodeRune(bs[i:]) switch r { @@ -57,12 +56,12 @@ func (s String) StringLength() int { return n } -func (n Number) StringLength() int { - return len(n) +func (num Number) StringLength() int { + return len(num) } -func (b Boolean) StringLength() int { - if b { +func (bol Boolean) StringLength() int { + if bol { return 4 } return 5 @@ -80,27 +79,27 @@ func (s *set) StringLength() int { return TermSliceStringLength(s.Slice(), 2) + 2 } -func (a *Array) StringLength() int { - if a.Len() == 0 { +func (arr *Array) StringLength() int { + if arr.Len() == 0 { return 2 // [] } // surrounding brackets + ", " for every element - 1 - return TermSliceStringLength(a.elems, 2) + 2 + return TermSliceStringLength(arr.elems, 2) + 2 } -func (o *object) StringLength() (n int) { - if o.Len() == 0 { +func (obj *object) StringLength() (n int) { + if obj.Len() == 0 { return 2 // {} } // ": " for every item + ", " for every item - 1 - o.Foreach(func(key, value *Term) { + obj.Foreach(func(key, value *Term) { n += key.StringLength() + 4 + value.StringLength() // ": " and ", " }) return n // surrounding {} but also minus last ", " } -func (l *lazyObj) StringLength() int { - return l.force().(*object).StringLength() +func (lob *lazyObj) StringLength() int { + return lob.force().(*object).StringLength() } func (ts *TemplateString) StringLength() (n int) { @@ -138,23 +137,23 @@ func comprehensionTermStringLength(t *Term) int { return t.StringLength() } -func (r Ref) StringLength() (n int) { - rlen := len(r) +func (ref Ref) StringLength() (n int) { + rlen := len(ref) if rlen == 0 { return 0 } - if s, ok := r[0].Value.(String); ok { + if s, ok := ref[0].Value.(String); ok { n = len(s) // first term should never be quoted } else { - n = r[0].StringLength() + n = ref[0].StringLength() } if rlen == 1 { return n } - for _, p := range r[1:] { + for _, p := range ref[1:] { switch v := p.Value.(type) { case String: str := string(v) @@ -177,113 +176,113 @@ func (v Var) StringLength() int { return len(v) } -func (s *SetComprehension) StringLength() int { - return comprehensionTermStringLength(s.Term) + s.Body.StringLength() + 5 // {} and " | " +func (sc *SetComprehension) StringLength() int { + return comprehensionTermStringLength(sc.Term) + sc.Body.StringLength() + 5 // {} and " | " } -func (a *ArrayComprehension) StringLength() int { - return comprehensionTermStringLength(a.Term) + a.Body.StringLength() + 5 // [] and " | " +func (ac *ArrayComprehension) StringLength() int { + return comprehensionTermStringLength(ac.Term) + ac.Body.StringLength() + 5 // [] and " | " } -func (o *ObjectComprehension) StringLength() (n int) { - n += comprehensionTermStringLength(o.Key) - n += comprehensionTermStringLength(o.Value) - n += o.Body.StringLength() +func (oc *ObjectComprehension) StringLength() (n int) { + n += comprehensionTermStringLength(oc.Key) + n += comprehensionTermStringLength(oc.Value) + n += oc.Body.StringLength() return n + 7 // "{}"", " | ", and ": " } -func (m *Module) StringLength() (n int) { - if m.Package != nil { - n += m.Package.StringLength() + 2 // newlines +func (mod *Module) StringLength() (n int) { + if mod.Package != nil { + n += mod.Package.StringLength() + 2 // newlines } - if len(m.Imports) > 0 { - for _, imp := range m.Imports { + if len(mod.Imports) > 0 { + for _, imp := range mod.Imports { n += imp.StringLength() + 1 // newline } } - if len(m.Rules) > 0 { - for _, rule := range m.Rules { - n += rule.stringLengthWithOpts(toStringOpts{regoVersion: m.regoVersion}) + 1 // newline + if len(mod.Rules) > 0 { + for _, rule := range mod.Rules { + n += rule.stringLengthWithOpts(toStringOpts{regoVersion: mod.regoVersion}) + 1 // newline } } return n } -func (p *Package) StringLength() int { - if p == nil { +func (pkg *Package) StringLength() int { + if pkg == nil { return 21 // } - if len(p.Path) <= 1 { - return 25 + p.Path.StringLength() // // package + if len(pkg.Path) <= 1 { + return 25 + pkg.Path.StringLength() // // package } - return 8 + p.Path[1:].StringLength() // "package ..." + return 8 + pkg.Path[1:].StringLength() // "package ..." } -func (i *Import) StringLength() (n int) { - n = 7 + i.Path.StringLength() // "import " and path - if i.Alias != "" { - n += 4 + i.Alias.StringLength() // " as " and alias +func (imp *Import) StringLength() (n int) { + n = 7 + imp.Path.StringLength() // "import " and path + if imp.Alias != "" { + n += 4 + imp.Alias.StringLength() // " as " and alias } return n } -func (r *Rule) StringLength() int { - return r.stringLengthWithOpts(toStringOpts{}) +func (rule *Rule) StringLength() int { + return rule.stringLengthWithOpts(toStringOpts{}) } -func (r *Rule) stringLengthWithOpts(opts toStringOpts) int { +func (rule *Rule) stringLengthWithOpts(opts toStringOpts) int { n := 0 - if r.Default { + if rule.Default { n += 8 // "default " } - n += r.Head.stringLengthWithOpts(opts) - if !r.Default { + n += rule.Head.stringLengthWithOpts(opts) + if !rule.Default { switch opts.RegoVersion() { case RegoV1, RegoV0CompatV1: n += 6 // " if { " default: n += 3 // " { " } - n += r.Body.StringLength() + 2 // body and closing " }" + n += rule.Body.StringLength() + 2 // body and closing " }" } - if r.Else != nil { - n += r.Else.stringLengthWithOpts(opts) + if rule.Else != nil { + n += rule.Else.stringLengthWithOpts(opts) } return n } -func (h *Head) StringLength() int { - return h.stringLengthWithOpts(toStringOpts{}) +func (head *Head) StringLength() int { + return head.stringLengthWithOpts(toStringOpts{}) } -func (h *Head) stringLengthWithOpts(opts toStringOpts) int { - n := h.Reference.StringLength() +func (head *Head) stringLengthWithOpts(opts toStringOpts) int { + n := head.Reference.StringLength() containsAdded := false switch { - case len(h.Args) != 0: - n += h.Args.StringLength() - case len(h.Reference) == 1 && h.Key != nil: + case len(head.Args) != 0: + n += head.Args.StringLength() + case len(head.Reference) == 1 && head.Key != nil: switch opts.RegoVersion() { case RegoV0: - n += 2 + h.Key.StringLength() // for [] + n += 2 + head.Key.StringLength() // for [] default: - n += 10 + h.Key.StringLength() // " contains " + n += 10 + head.Key.StringLength() // " contains " containsAdded = true } } - if h.Value != nil { - if h.Assign { + if head.Value != nil { + if head.Assign { n += 4 // " := " } else { n += 3 // " = " } - n += h.Value.StringLength() - } else if !containsAdded && h.Name == "" && h.Key != nil { - n += 10 + h.Key.StringLength() // " contains " + n += head.Value.StringLength() + } else if !containsAdded && head.Name == "" && head.Key != nil { + n += 10 + head.Key.StringLength() // " contains " } return n } @@ -296,20 +295,20 @@ func (a Args) StringLength() (n int) { return n - 2 // minus last ", " } -func (b Body) StringLength() (n int) { - for _, expr := range b { +func (body Body) StringLength() (n int) { + for _, expr := range body { n += expr.StringLength() + 2 // "; " } return max(n-2, 0) // minus last "; " (if `n` isn't 0) } -func (e *Expr) StringLength() (n int) { - if e.Negated { +func (expr *Expr) StringLength() (n int) { + if expr.Negated { n += 4 // "not " } - switch terms := e.Terms.(type) { + switch terms := expr.Terms.(type) { case []*Term: - if e.IsEquality() && validEqAssignArgCount(e) { + if expr.IsEquality() && validEqAssignArgCount(expr) { n += terms[1].StringLength() + len(Equality.Infix) + terms[2].StringLength() + 2 // spaces around = } else { n += Call(terms).StringLength() @@ -317,10 +316,10 @@ func (e *Expr) StringLength() (n int) { case StringLengther: n += terms.StringLength() default: - panic(fmt.Sprintf("string length estimation not implemented for type: %T", e.Terms)) + panic(fmt.Sprintf("string length estimation not implemented for type: %T", expr.Terms)) } - for _, w := range e.With { + for _, w := range expr.With { n += w.StringLength() + 1 // space before with } @@ -331,20 +330,20 @@ func (w *With) StringLength() int { return w.Target.StringLength() + w.Value.StringLength() + 9 // "with " and " as " } -func (e *Every) StringLength() int { +func (q *Every) StringLength() int { n := 6 // "every " - if e.Key != nil { - n += e.Key.StringLength() + 2 // ", " + if q.Key != nil { + n += q.Key.StringLength() + 2 // ", " } - n += e.Value.StringLength() + 4 // " in " - n += e.Domain.StringLength() + 3 // " { " - n += e.Body.StringLength() + 2 // " }" + n += q.Value.StringLength() + 4 // " in " + n += q.Domain.StringLength() + 3 // " { " + n += q.Body.StringLength() + 2 // " }" return n } -func (s *SomeDecl) StringLength() int { +func (d *SomeDecl) StringLength() int { n := 5 // "some " - if call, ok := s.Symbols[0].Value.(Call); ok { + if call, ok := d.Symbols[0].Value.(Call); ok { n += 4 // " in " n += call[1].StringLength() if len(call) == 4 { @@ -356,24 +355,24 @@ func (s *SomeDecl) StringLength() int { } return n } - return n + TermSliceStringLength(s.Symbols, 2) + return n + TermSliceStringLength(d.Symbols, 2) } func (c *Comment) StringLength() int { return 1 + len(c.Text) // '#' + text } -func (not *Not) StringLength() int { - if !not.ExplicitBody && len(not.Body) == 1 { - if notBodyNeedsParens(not.Body) { +func (n *Not) StringLength() int { + if !n.ExplicitBody && len(n.Body) == 1 { + if notBodyNeedsParens(n.Body) { // "not (...)" - return 6 + not.Body.StringLength() + return 6 + n.Body.StringLength() } // "not ..." - return 4 + not.Body.StringLength() + return 4 + n.Body.StringLength() } // "not {...}" - return 6 + not.Body.StringLength() + return 6 + n.Body.StringLength() } func (a *LogicalAnd) StringLength() int { diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/syncpools.go b/vendor/github.com/open-policy-agent/opa/v1/ast/syncpools.go index 500bb073cf..f540f4a65e 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/syncpools.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/syncpools.go @@ -2,15 +2,17 @@ package ast import ( "bytes" + "strings" "sync" "github.com/open-policy-agent/opa/v1/util" ) var ( - TermPtrPool = util.NewSyncPool[Term]() - BytesReaderPool = util.NewSyncPool[bytes.Reader]() - IndexResultPool = util.NewSyncPool[IndexResult]() + TermPtrPool = util.NewSyncPool[Term]() + BytesReaderPool = util.NewSyncPool[bytes.Reader]() + StringReaderPool = util.NewSyncPool[strings.Reader]() + IndexResultPool = util.NewSyncPool[IndexResult]() // Needs custom pool because of custom Put logic. varVisitorPool = &vvPool{ diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/term.go b/vendor/github.com/open-policy-agent/opa/v1/ast/term.go index 23820f13bb..6e4503d4d8 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/term.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/term.go @@ -75,7 +75,7 @@ func InterfaceToValue(x any) (Value, error) { case nil: return NullValue, nil case bool: - return InternedValue(x), nil + return internedBooleanValue(x), nil case json.Number: if interned := InternedIntNumberTermFromString(string(x)); interned != nil { return interned.Value, nil @@ -90,7 +90,7 @@ func InterfaceToValue(x any) (Value, error) { case float64: return floatNumber(x), nil case string: - return String(x), nil + return internedStringValue(x), nil case []any: r := util.NewPtrSlice[Term](len(x)) for i, e := range x { @@ -104,14 +104,14 @@ func InterfaceToValue(x any) (Value, error) { case []string: r := util.NewPtrSlice[Term](len(x)) for i, e := range x { - r[i].Value = String(e) + r[i].Value = internedStringValue(e) } return NewArray(r...), nil case map[string]any: kvs := util.NewPtrSlice[Term](len(x) * 2) idx := 0 for k, v := range x { - kvs[idx].Value = String(k) + kvs[idx].Value = internedStringValue(k) v, err := InterfaceToValue(v) if err != nil { return nil, err @@ -125,11 +125,7 @@ func InterfaceToValue(x any) (Value, error) { } return NewObject(tuples...), nil case map[string]string: - r := newobject(len(x)) - for k, v := range x { - r.Insert(StringTerm(k), StringTerm(v)) - } - return r, nil + return MapToObject(x, nil, InternedTerm), nil default: ptr := util.Reference(x) if err := util.RoundTrip(ptr); err != nil { @@ -150,7 +146,12 @@ func ValueFromReader(r io.Reader) (Value, error) { // As converts v into a Go native type referred to by x. func As(v Value, x any) error { - return util.NewJSONDecoder(strings.NewReader(v.String())).Decode(x) + sr := StringReaderPool.Get() + defer StringReaderPool.Put(sr) + + sr.Reset(v.String()) + + return util.NewJSONDecoder(sr).Decode(x) } // Resolver defines the interface for resolving references to native Go values. @@ -201,7 +202,7 @@ func valueToInterface(v Value, resolver Resolver, opt JSONOpt) (any, error) { case String: return string(v), nil case *Array: - buf := []any{} + buf := make([]any, 0, v.Len()) for i := range v.Len() { x1, err := valueToInterface(v.Elem(i).Value, resolver, opt) if err != nil { @@ -349,44 +350,46 @@ func (term *Term) Copy() *Term { } cpy := *term + cpy.Value = CopyValue(term.Value) - switch v := term.Value.(type) { + return &cpy +} + +// CopyValue returns a deep copy of v. The Value interface doesn't require a +// Copy method, so this dispatches on the known value types. Values of any other +// type are returned as-is. +func CopyValue(v Value) Value { + switch v := v.(type) { case Null, Boolean, Number, String, Var: - cpy.Value = v + // Scalars are immutable, no copy needed. + return v case Ref: - cpy.Value = v.Copy() + return v.Copy() case *Array: - cpy.Value = v.Copy() + return v.Copy() case Set: - cpy.Value = v.Copy() + return v.Copy() case *object: - cpy.Value = v.Copy() + return v.Copy() case *ArrayComprehension: - cpy.Value = v.Copy() + return v.Copy() case *ObjectComprehension: - cpy.Value = v.Copy() + return v.Copy() case *SetComprehension: - cpy.Value = v.Copy() + return v.Copy() case *TemplateString: - cpy.Value = v.Copy() + return v.Copy() case Call: - cpy.Value = v.Copy() + return v.Copy() } - return &cpy + return v } // Equal returns true if this term equals the other term. Equality is // defined for each kind of term, and does not compare the Location. func (term *Term) Equal(other *Term) bool { - if term == other { - return true - } - if term == nil || other == nil { - return false - } - - return ValueEqual(term.Value, other.Value) + return term == other || (term != nil && other != nil && ValueEqual(term.Value, other.Value)) } // Get returns a value referred to by name from the term. @@ -430,6 +433,14 @@ func (term *Term) Vars() VarSet { return vis.vars } +// TermValueIs is a functional predicate to check if the term's Value is of type T. +func TermValueIs[T Value](term *Term) (ok bool) { + if ok = term != nil; ok { + _, ok = term.Value.(T) + } + return ok +} + // IsConstant returns true if the AST value is constant. // Note that this is only a shallow check as we currently don't have a real // notion of constant "vars" in the AST implementation. Meaning that while we could @@ -624,12 +635,8 @@ func NullTerm() *Term { // Equal returns true if the other term Value is also Null. func (Null) Equal(other Value) bool { - switch other.(type) { - case Null: - return true - default: - return false - } + _, ok := other.(Null) + return ok } // Compare compares null to other, return <0, 0, or >0 if it is less than, equal to, @@ -673,12 +680,8 @@ func BooleanTerm(b bool) *Term { // Equal returns true if the other Value is a Boolean and is equal. func (bol Boolean) Equal(other Value) bool { - switch other := other.(type) { - case Boolean: - return bol == other - default: - return false - } + _, ok := other.(Boolean) + return ok && bol == other } // Compare compares bol to other, return <0, 0, or >0 if it is less than, equal to, @@ -854,9 +857,6 @@ func (str String) Equal(other Value) bool { // Compare compares str to other, return <0, 0, or >0 if it is less than, equal to, // or greater than other. func (str String) Compare(other Value) int { - // Optimize for the common case of one string being compared to another by - // using a direct comparison of values. This avoids the allocation performed - // when calling Compare and its any argument conversion. if otherStr, ok := other.(String); ok { if str == otherStr { return 0 @@ -1332,20 +1332,12 @@ func (ref Ref) DynamicSuffix() Ref { // IsGround returns true if all of the parts of the Ref are ground. func (ref Ref) IsGround() bool { - if len(ref) < 2 { - return true - } - return termSliceIsGround(ref[1:]) + return len(ref) < 2 || util.Every(ref[1:], (*Term).IsGround) } // IsNested returns true if this ref contains other Refs. func (ref Ref) IsNested() bool { - for _, x := range ref { - if _, ok := x.Value.(Ref); ok { - return true - } - } - return false + return slices.ContainsFunc(ref, TermValueIs[Ref]) } // Ptr returns a slash-separated path string for this ref. If the ref @@ -1467,7 +1459,7 @@ func NewArray(a ...*Term) *Array { for i, e := range a { hs[i] = e.Value.Hash() } - arr := &Array{elems: a, hashs: hs, ground: termSliceIsGround(a)} + arr := &Array{elems: a, hashs: hs, ground: util.Every(a, (*Term).IsGround)} arr.rehash() return arr } @@ -1643,7 +1635,7 @@ func (arr *Array) Slice(i, j int) *Array { } // If arr is ground, the slice is, too. // If it's not, the slice could still be. - gr := arr.ground || termSliceIsGround(elems) + gr := arr.ground || util.Every(elems, (*Term).IsGround) s := &Array{elems: elems, hashs: hashs, ground: gr} s.rehash() @@ -1821,9 +1813,11 @@ func (s *set) Find(path Ref) (Value, error) { } // Diff returns elements in s that are not in other. +// A returned empty set will be an interned representation that +// should not be modified without copying. func (s *set) Diff(other Set) Set { if s.Compare(other) == 0 { - return NewSet() + return InternedEmptySetValue.(Set) } result := newset(len(s.keys)) @@ -1918,13 +1912,9 @@ func (s *set) Map(f func(*Term) (*Term, error)) (Set, error) { // argument to f is the reduced value (starting with i) and the second argument // to f is the element in s. func (s *set) Reduce(i *Term, f func(*Term, *Term) (*Term, error)) (*Term, error) { - err := s.Iter(func(x *Term) error { - var err error + err := s.Iter(func(x *Term) (err error) { i, err = f(i, x) - if err != nil { - return err - } - return nil + return err }) return i, err } @@ -2028,10 +2018,44 @@ type Object interface { // NewObject creates a new Object with t. func NewObject(t ...[2]*Term) Object { - obj := newobject(len(t)) - for i := range t { - obj.insert(t[i][0], t[i][1], false) + var keys []*objectElem + n := len(t) + if n > 0 { + keys = make([]*objectElem, n) } + obj := &object{ + elems: make(map[int]*objectElem, n), + keys: keys, + sortGuard: sync.Once{}, + } + + // NOTE(anders): The code below is convoluted, but necessary + // since creating objects is something we do a lot and often + // on hot paths, this avoids allocating one objectElem per + // key-value pair, in favor of a single contiguous block of + // memory. The same technique is used in (*object).Copy(), + // for the same reasons. + elems := make([]objectElem, n) + for i, kv := range t { + key, val := kv[0], kv[1] + elems[i] = objectElem{key: key, value: val} + obj.keys[i] = &elems[i] + + keyHash := key.Hash() + if head, ok := obj.elems[keyHash]; ok { + elems[i].next = head + } + obj.hash += keyHash + val.Hash() + + if key.IsGround() { + obj.ground++ + } + if val.IsGround() { + obj.ground++ + } + obj.elems[keyHash] = &elems[i] + } + return obj } @@ -2055,72 +2079,72 @@ type lazyObj struct { native map[string]any } -func (l *lazyObj) force() Object { - if l.strict == nil { - l.strict = MustInterfaceToValue(l.native).(Object) +func (lob *lazyObj) force() Object { + if lob.strict == nil { + lob.strict = MustInterfaceToValue(lob.native).(Object) // NOTE(jf): a possible performance improvement here would be to check how many // entries have been realized to AST in the cache, and if some threshold compared to the // total number of keys is exceeded, realize the remaining entries and set l.strict to l.cache. - l.cache = map[string]Value{} // We don't need the cache anymore; drop it to free up memory. + lob.cache = map[string]Value{} // We don't need the cache anymore; drop it to free up memory. } - return l.strict + return lob.strict } -func (l *lazyObj) Compare(other Value) int { - if c := valueTypeCompare(l, other); c != 0 { +func (lob *lazyObj) Compare(other Value) int { + if c := valueTypeCompare(lob, other); c != 0 { return c } - return l.force().Compare(other) + return lob.force().Compare(other) } -func (l *lazyObj) Copy() Object { - return l +func (lob *lazyObj) Copy() Object { + return lob } -func (l *lazyObj) Diff(other Object) Object { - return l.force().Diff(other) +func (lob *lazyObj) Diff(other Object) Object { + return lob.force().Diff(other) } -func (l *lazyObj) Intersect(other Object) [][3]*Term { - return l.force().Intersect(other) +func (lob *lazyObj) Intersect(other Object) [][3]*Term { + return lob.force().Intersect(other) } -func (l *lazyObj) Iter(f func(*Term, *Term) error) error { - return l.force().Iter(f) +func (lob *lazyObj) Iter(f func(*Term, *Term) error) error { + return lob.force().Iter(f) } -func (l *lazyObj) Until(f func(*Term, *Term) bool) bool { +func (lob *lazyObj) Until(f func(*Term, *Term) bool) bool { // NOTE(sr): there could be benefits in not forcing here -- if we abort because // `f` returns true, we could save us from converting the rest of the object. - return l.force().Until(f) + return lob.force().Until(f) } -func (l *lazyObj) Foreach(f func(*Term, *Term)) { - l.force().Foreach(f) +func (lob *lazyObj) Foreach(f func(*Term, *Term)) { + lob.force().Foreach(f) } -func (l *lazyObj) Filter(filter Object) (Object, error) { - return l.force().Filter(filter) +func (lob *lazyObj) Filter(filter Object) (Object, error) { + return lob.force().Filter(filter) } -func (l *lazyObj) Map(f func(*Term, *Term) (*Term, *Term, error)) (Object, error) { - return l.force().Map(f) +func (lob *lazyObj) Map(f func(*Term, *Term) (*Term, *Term, error)) (Object, error) { + return lob.force().Map(f) } -func (l *lazyObj) Merge(other Object) (Object, bool) { - return l.force().Merge(other) +func (lob *lazyObj) Merge(other Object) (Object, bool) { + return lob.force().Merge(other) } -func (l *lazyObj) MergeWith(other Object, conflictResolver func(v1, v2 *Term) (*Term, bool)) (Object, bool) { - return l.force().MergeWith(other, conflictResolver) +func (lob *lazyObj) MergeWith(other Object, conflictResolver func(v1, v2 *Term) (*Term, bool)) (Object, bool) { + return lob.force().MergeWith(other, conflictResolver) } -func (l *lazyObj) Len() int { - return len(l.native) +func (lob *lazyObj) Len() int { + return len(lob.native) } -func (l *lazyObj) String() string { - return l.force().String() +func (lob *lazyObj) String() string { + return lob.force().String() } // get is merely there to implement the Object interface -- `get` there serves the @@ -2129,16 +2153,16 @@ func (*lazyObj) get(*Term) *objectElem { return nil } -func (l *lazyObj) Get(k *Term) *Term { - if l.strict != nil { - return l.strict.Get(k) +func (lob *lazyObj) Get(k *Term) *Term { + if lob.strict != nil { + return lob.strict.Get(k) } if s, ok := k.Value.(String); ok { - if v, ok := l.cache[string(s)]; ok { + if v, ok := lob.cache[string(s)]; ok { return NewTerm(v) } - if val, ok := l.native[string(s)]; ok { + if val, ok := lob.native[string(s)]; ok { var converted Value switch val := val.(type) { case map[string]any: @@ -2146,40 +2170,39 @@ func (l *lazyObj) Get(k *Term) *Term { default: converted = MustInterfaceToValue(val) } - l.cache[string(s)] = converted + lob.cache[string(s)] = converted return NewTerm(converted) } } return nil } -func (l *lazyObj) Insert(k, v *Term) { - l.force().Insert(k, v) +func (lob *lazyObj) Insert(k, v *Term) { + lob.force().Insert(k, v) } func (*lazyObj) IsGround() bool { return true } -func (l *lazyObj) Hash() int { - return l.force().Hash() +func (lob *lazyObj) Hash() int { + return lob.force().Hash() } -func (l *lazyObj) Keys() []*Term { - if l.strict != nil { - return l.strict.Keys() +func (lob *lazyObj) Keys() []*Term { + if lob.strict != nil { + return lob.strict.Keys() } - ret := make([]*Term, 0, len(l.native)) - for k := range l.native { - ret = append(ret, StringTerm(k)) + ret := make([]*Term, 0, len(lob.native)) + for k := range lob.native { + ret = append(ret, InternedTerm(k)) } - slices.SortFunc(ret, TermValueCompare) - return ret + return util.SortedFunc(ret, TermValueCompare) } -func (l *lazyObj) KeysIterator() ObjectKeysIterator { - return &lazyObjKeysIterator{keys: l.Keys()} +func (lob *lazyObj) KeysIterator() ObjectKeysIterator { + return &lazyObjKeysIterator{keys: lob.Keys()} } type lazyObjKeysIterator struct { @@ -2195,19 +2218,19 @@ func (ki *lazyObjKeysIterator) Next() (*Term, bool) { return ki.keys[ki.current-1], true } -func (l *lazyObj) Find(path Ref) (Value, error) { - if l.strict != nil { - return l.strict.Find(path) +func (lob *lazyObj) Find(path Ref) (Value, error) { + if lob.strict != nil { + return lob.strict.Find(path) } if len(path) == 0 { - return l, nil + return lob, nil } if p0, ok := path[0].Value.(String); ok { - if v, ok := l.cache[string(p0)]; ok { + if v, ok := lob.cache[string(p0)]; ok { return v.Find(path[1:]) } - if v, ok := l.native[string(p0)]; ok { + if v, ok := lob.native[string(p0)]; ok { var converted Value switch v := v.(type) { case map[string]any: @@ -2215,7 +2238,7 @@ func (l *lazyObj) Find(path Ref) (Value, error) { default: converted = MustInterfaceToValue(v) } - l.cache[string(p0)] = converted + lob.cache[string(p0)] = converted return converted.Find(path[1:]) } } @@ -2292,6 +2315,38 @@ func (obj *object) Compare(other Value) int { return len(akeys) - len(bkeys) } +func (obj *object) Equal(other Value) bool { + var ob2 *object + switch v := other.(type) { + case *object: + ob2 = v + case *lazyObj: + return obj.Equal(v.force()) + } + + if obj == ob2 { + return true + } + if obj == nil || ob2 == nil || len(obj.keys) != len(ob2.keys) { + return false + } + elems1, elems2 := obj.sortedKeys(), ob2.sortedKeys() + // Note(anderseknert): + // Go can't (easily) know that the above calls don't modify the length + // checked before. Doing it once more here is cheap and ensures that the + // loop is evaluated without additional nil and bounds checks + if len(elems1) != len(elems2) { + return false + } + + for i, elem := range elems1 { + if !elem.key.Equal(elems2[i].key) || !elem.value.Equal(elems2[i].value) { + return false + } + } + return true +} + // Find returns the value at the key or undefined. func (obj *object) Find(path Ref) (Value, error) { if len(path) == 0 { @@ -2371,11 +2426,13 @@ func (obj *object) Copy() Object { return cpy } - // Batch-allocate all objectElems, keys, and values in contiguous blocks - // (3 allocations instead of 3N). + // Batch-allocate all objectElems and keys/value pairs in contiguous blocks + // (2 allocations instead of 3N). elems := make([]objectElem, n) - keys := make([]Term, n) - vals := make([]Term, n) + pairs := make([]Term, n*2) + keys := pairs[:n] + vals := pairs[n:] + cpy.keys = make([]*objectElem, n) for i, srcElem := range obj.keys { @@ -2942,7 +2999,7 @@ func (c Call) Hash() int { // IsGround returns true if the Value is ground. func (c Call) IsGround() bool { - return termSliceIsGround(c) + return util.Every(c, (*Term).IsGround) } // MakeExpr returns a new Expr from this call. @@ -3035,15 +3092,6 @@ func termSliceHash(a []*Term) int { return hash } -func termSliceIsGround(a []*Term) bool { - for _, v := range a { - if !v.IsGround() { - return false - } - } - return true -} - // Detect when String() need to use expensive JSON‐escaped form func isControlOrBackslash(r rune) bool { return r == '\\' || unicode.IsControl(r) diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/term_appenders.go b/vendor/github.com/open-policy-agent/opa/v1/ast/term_appenders.go index 60a9a088d7..1ae32fb201 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/term_appenders.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/term_appenders.go @@ -26,8 +26,8 @@ func (v Var) AppendText(buf []byte) ([]byte, error) { return append(buf, v...), nil } -func (b Boolean) AppendText(buf []byte) ([]byte, error) { - if b { +func (bol Boolean) AppendText(buf []byte) ([]byte, error) { + if bol { return append(buf, "true"...), nil } return append(buf, "false"...), nil @@ -92,8 +92,8 @@ func (obj *object) AppendText(buf []byte) ([]byte, error) { return append(buf, '}'), nil } -func (obj *lazyObj) AppendText(buf []byte) ([]byte, error) { - return append(buf, obj.force().String()...), nil +func (lob *lazyObj) AppendText(buf []byte) ([]byte, error) { + return append(buf, lob.force().String()...), nil } func (s *set) AppendText(buf []byte) ([]byte, error) { @@ -173,30 +173,30 @@ func (ts *TemplateString) AppendText(buf []byte) ([]byte, error) { return append(buf, '"'), nil } -func (r Ref) AppendText(buf []byte) ([]byte, error) { - reflen := len(r) +func (ref Ref) AppendText(buf []byte) ([]byte, error) { + reflen := len(ref) if reflen == 0 { return buf, nil } if reflen == 1 { - if s, ok := r[0].Value.(String); ok { + if s, ok := ref[0].Value.(String); ok { // While a ref head is typically a Var, a lone String term should not be quoted return append(buf, s...), nil } - return r[0].AppendText(buf) + return ref[0].AppendText(buf) } - if name, ok := BuiltinNameFromRef(r); ok { + if name, ok := BuiltinNameFromRef(ref); ok { return append(buf, name...), nil } var err error - if s, ok := r[0].Value.(String); ok { + if s, ok := ref[0].Value.(String); ok { buf = append(buf, s...) - } else if buf, err = r[0].AppendText(buf); err != nil { + } else if buf, err = ref[0].AppendText(buf); err != nil { return nil, err } - for _, p := range r[1:] { + for _, p := range ref[1:] { switch v := p.Value.(type) { case String: str := string(v) @@ -287,23 +287,23 @@ func appendComprehensionTerm(buf []byte, term *Term) ([]byte, error) { return term.AppendText(buf) } -func (not *Not) AppendText(buf []byte) ([]byte, error) { - if !not.ExplicitBody && len(not.Body) == 1 { - if notBodyNeedsParens(not.Body) { +func (n *Not) AppendText(buf []byte) ([]byte, error) { + if !n.ExplicitBody && len(n.Body) == 1 { + if notBodyNeedsParens(n.Body) { buf = append(buf, "not ("...) var err error - if buf, err = not.Body.AppendText(buf); err != nil { + if buf, err = n.Body.AppendText(buf); err != nil { return nil, err } return append(buf, ')'), nil } buf = append(buf, "not "...) - return not.Body.AppendText(buf) + return n.Body.AppendText(buf) } buf = append(buf, "not {"...) var err error - if buf, err = not.Body.AppendText(buf); err != nil { + if buf, err = n.Body.AppendText(buf); err != nil { return nil, err } return append(buf, '}'), nil diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/term_json.go b/vendor/github.com/open-policy-agent/opa/v1/ast/term_json.go index 685801b66d..463b5d9702 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/term_json.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/term_json.go @@ -65,8 +65,8 @@ func (s *set) MarshalJSON() ([]byte, error) { return json.Marshal(s.sortedKeys()) } -func (l *lazyObj) MarshalJSON() ([]byte, error) { - return l.force().(*object).MarshalJSON() +func (lob *lazyObj) MarshalJSON() ([]byte, error) { + return lob.force().(*object).MarshalJSON() } func (n *Not) MarshalJSON() ([]byte, error) { diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/term_jsonv2.go b/vendor/github.com/open-policy-agent/opa/v1/ast/term_jsonv2.go index 62189c3729..462c8bdee2 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/term_jsonv2.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/term_jsonv2.go @@ -47,8 +47,8 @@ var ( // These are here to ensure that we do not fall down to TextAppender, which // Go 1.27's encoding/json would otherwise use, encoding these as JSON strings. -func (b Boolean) MarshalJSONTo(e *jsontext.Encoder) error { - return e.WriteToken(jsontext.Bool(bool(b))) +func (bol Boolean) MarshalJSONTo(e *jsontext.Encoder) error { + return e.WriteToken(jsontext.Bool(bool(bol))) } func (Null) MarshalJSONTo(e *jsontext.Encoder) error { @@ -80,24 +80,24 @@ func (str String) MarshalJSONTo(e *jsontext.Encoder) error { return e.WriteToken(jsontext.String(string(str))) } -func (t *Term) MarshalJSONTo(e *jsontext.Encoder) (err error) { +func (term *Term) MarshalJSONTo(e *jsontext.Encoder) (err error) { // Token write errors are unchecked: an unbalanced value fails at the closing // token. A marshaller can fail having written a balanced value, so is checked. e.WriteToken(jsontext.BeginObject) includeLocation := astJSON.GetOptions().MarshalOptions.IncludeLocation - if t.Location != nil && includeLocation.Term { - if err := jsonv2.WriteField(e, "location", t.Location); err != nil { + if term.Location != nil && includeLocation.Term { + if err := jsonv2.WriteField(e, "location", term.Location); err != nil { return err } } e.WriteToken(jsontext.String("type")) - e.WriteToken(jsontext.String(ValueName(t.Value))) + e.WriteToken(jsontext.String(ValueName(term.Value))) e.WriteToken(jsontext.String("value")) - if err = marshalValueTo(e, t.Value); err != nil { - return fmt.Errorf("failed to marshal term of %s: %w", ValueName(t.Value), err) + if err = marshalValueTo(e, term.Value); err != nil { + return fmt.Errorf("failed to marshal term of %s: %w", ValueName(term.Value), err) } return e.WriteToken(jsontext.EndObject) @@ -108,23 +108,23 @@ func (term *Term) MarshalJSON() ([]byte, error) { return jsonv2.MarshalMarshalerTo(term) } -func (r Ref) MarshalJSONTo(e *jsontext.Encoder) (err error) { - return jsonv2.WriteMarshalerToArrayOrNull(e, r) +func (ref Ref) MarshalJSONTo(e *jsontext.Encoder) (err error) { + return jsonv2.WriteMarshalerToArrayOrNull(e, ref) } -func (t *TemplateString) MarshalJSONTo(e *jsontext.Encoder) (err error) { +func (ts *TemplateString) MarshalJSONTo(e *jsontext.Encoder) (err error) { // Token write errors are unchecked: an unbalanced value fails at the closing // token. A marshaller can fail having written a balanced value, so is checked. e.WriteToken(jsontext.BeginObject) e.WriteToken(jsontext.String("parts")) - if t.Parts == nil { + if ts.Parts == nil { // Parts has no omitempty tag, so it's always written. Matches // encoding/json v1, which encodes a nil slice as null rather than as an // empty array. e.WriteToken(jsontext.Null) } else { e.WriteToken(jsontext.BeginArray) - for _, p := range t.Parts { + for _, p := range ts.Parts { switch v := p.(type) { case *Expr: if err := v.MarshalJSONTo(e); err != nil { @@ -140,7 +140,7 @@ func (t *TemplateString) MarshalJSONTo(e *jsontext.Encoder) (err error) { } e.WriteToken(jsontext.String("multi_line")) - e.WriteToken(jsontext.Bool(t.MultiLine)) + e.WriteToken(jsontext.Bool(ts.MultiLine)) return e.WriteToken(jsontext.EndObject) } @@ -197,12 +197,12 @@ func (obj *object) MarshalJSONTo(e *jsontext.Encoder) error { return e.WriteToken(jsontext.EndArray) } -func (l *lazyObj) MarshalJSONTo(e *jsontext.Encoder) error { - return l.force().(*object).MarshalJSONTo(e) +func (lob *lazyObj) MarshalJSONTo(e *jsontext.Encoder) error { + return lob.force().(*object).MarshalJSONTo(e) } -func (l *lazyObj) MarshalJSON() ([]byte, error) { - return l.force().(*object).MarshalJSON() +func (lob *lazyObj) MarshalJSON() ([]byte, error) { + return lob.force().(*object).MarshalJSON() } // MarshalJSON returns JSON encoded bytes representing obj. @@ -210,8 +210,8 @@ func (obj *object) MarshalJSON() ([]byte, error) { return jsonv2.MarshalMarshalerTo(obj) } -func (a *Array) MarshalJSONTo(e *jsontext.Encoder) error { - return jsonv2.WriteMarshalerToArray(e, a.elems) +func (arr *Array) MarshalJSONTo(e *jsontext.Encoder) error { + return jsonv2.WriteMarshalerToArray(e, arr.elems) } // MarshalJSON returns JSON encoded bytes representing arr. diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/transform.go b/vendor/github.com/open-policy-agent/opa/v1/ast/transform.go index 7c0c54e3d7..84e9700018 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/transform.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/transform.go @@ -89,14 +89,10 @@ func Transform(t Transformer, x any) (any, error) { } return y, nil case *Import: - y.Path, err = transformTerm(t, y.Path) - if err != nil { - return nil, err + if y.Path, err = transformTerm(t, y.Path); err == nil { + y.Alias, err = transformVar(t, y.Alias) } - if y.Alias, err = transformVar(t, y.Alias); err != nil { - return nil, err - } - return y, nil + return y, err case *Rule: if y.Head, err = transformHead(t, y.Head); err != nil { return nil, err diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/treenode_dump.go b/vendor/github.com/open-policy-agent/opa/v1/ast/treenode_dump.go index f22367bc81..0b7c5a6e60 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/treenode_dump.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/treenode_dump.go @@ -2,8 +2,9 @@ package ast import ( "fmt" - "sort" "strings" + + "github.com/open-policy-agent/opa/v1/util" ) // Dump returns a string representation of the tree structure rooted at this node. @@ -24,21 +25,16 @@ func (n *TreeNode) dumpRecursive(sb *strings.Builder, prefix, childPrefix string fmt.Fprintf(sb, " ext:%v", n.External.Ref) } if len(n.Values) > 0 { - fmt.Fprintf(sb, " rules:%d", len(n.Values)) + sb.WriteString(" rules:") + util.WriteInt(sb, len(n.Values)) } - sb.WriteString("\n") + sb.WriteByte('\n') if len(n.Children) == 0 { return } - keys := make([]Value, 0, len(n.Children)) - for k := range n.Children { - keys = append(keys, k) - } - sort.Slice(keys, func(i, j int) bool { - return Compare(keys[i], keys[j]) < 0 - }) + keys := util.SortedFunc(util.Keys(n.Children), Value.Compare) for i, key := range keys { child := n.Children[key] diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/varset.go b/vendor/github.com/open-policy-agent/opa/v1/ast/varset.go index 55bbea80d0..582a9982ec 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/varset.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/varset.go @@ -6,7 +6,6 @@ package ast import ( "fmt" - "slices" "github.com/open-policy-agent/opa/v1/util" ) @@ -111,12 +110,7 @@ func (s VarSet) Intersect(vs VarSet) VarSet { // Sorted returns a new sorted slice of vars from s. func (s VarSet) Sorted() []Var { - sorted := make([]Var, 0, len(s)) - for v := range s { - sorted = append(sorted, v) - } - slices.SortFunc(sorted, VarCompare) - return sorted + return util.SortedFunc(util.Keys(s), VarCompare) } // Update merges the other VarSet into this VarSet. diff --git a/vendor/github.com/open-policy-agent/opa/v1/ast/version_index.json b/vendor/github.com/open-policy-agent/opa/v1/ast/version_index.json index 91e4d09ff3..d6d298cbb9 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ast/version_index.json +++ b/vendor/github.com/open-policy-agent/opa/v1/ast/version_index.json @@ -1070,6 +1070,11 @@ } }, "keywords": { + "and": { + "Major": 1, + "Minor": 20, + "Patch": 0 + }, "contains": { "Major": 0, "Minor": 42, @@ -1094,6 +1099,11 @@ "Major": 1, "Minor": 17, "Patch": 0 + }, + "or": { + "Major": 1, + "Minor": 20, + "Patch": 0 } } } diff --git a/vendor/github.com/open-policy-agent/opa/v1/bundle/bundle.go b/vendor/github.com/open-policy-agent/opa/v1/bundle/bundle.go index f697e336d5..c56cd122a1 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/bundle/bundle.go +++ b/vendor/github.com/open-policy-agent/opa/v1/bundle/bundle.go @@ -361,8 +361,8 @@ func (m *Manifest) validateAndInjectDefaults(b Bundle) error { // Validate modules in bundle. for _, module := range b.Modules { found := false - if path, err := module.Parsed.Package.Path.Ptr(); err == nil { - found = RootPathsContain(roots, path) + if path, err := storage.NewPathForRef(module.Parsed.Package.Path); err == nil { + found = rootPathsContainSegments(roots, path) } if !found { return fmt.Errorf("manifest roots %v do not permit '%v' in module '%s'", roots, module.Parsed.Package, module.Path) @@ -1695,7 +1695,14 @@ func RootPathsOverlap(pathA string, pathB string) bool { // RootPathsContain takes a set of bundle root paths and returns true if the path is contained. func RootPathsContain(roots []string, path string) bool { - segments := rootPathSegments(path) + return rootPathsContainSegments(roots, rootPathSegments(path)) +} + +// rootPathsContainSegments is RootPathsContain for a path that's already split +// into segments. Manifest roots are raw, unescaped strings, so callers holding +// a ref or storage path must pass its unescaped segments rather than the +// percent-encoded form produced by ast.Ref.Ptr or storage.Path.String. +func rootPathsContainSegments(roots []string, segments []string) bool { for i := range roots { if rootContains(rootPathSegments(roots[i]), segments) { return true @@ -1815,10 +1822,8 @@ func preProcessBundle(loader DirectoryLoader, skipVerify bool, sizeLimitBytes in base := filepath.Base(f.Path()) if base == patchFile { - - var b bytes.Buffer - tee := io.TeeReader(f.reader, &b) - f.reader = tee + b := new(bytes.Buffer) + f.reader = io.TeeReader(f.reader, b) buf, err := readFile(f, sizeLimitBytes) if err != nil { @@ -1829,7 +1834,7 @@ func preProcessBundle(loader DirectoryLoader, skipVerify bool, sizeLimitBytes in return bundle, nil, fmt.Errorf("bundle load failed on patch decode: %w", err) } - f.reader = &b + f.reader = b } } } diff --git a/vendor/github.com/open-policy-agent/opa/v1/bundle/file.go b/vendor/github.com/open-policy-agent/opa/v1/bundle/file.go index e4f74cc0e4..beb2e46645 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/bundle/file.go +++ b/vendor/github.com/open-policy-agent/opa/v1/bundle/file.go @@ -3,17 +3,19 @@ package bundle import ( "archive/tar" "bytes" + "cmp" "compress/gzip" "fmt" "io" "io/fs" "os" "path/filepath" - "sort" + "slices" "strings" "sync" "github.com/open-policy-agent/opa/v1/loader/filter" + "github.com/open-policy-agent/opa/v1/util" "github.com/open-policy-agent/opa/v1/storage" ) @@ -193,10 +195,7 @@ func (d *dirLoader) WithFollowSymlinks(followSymlinks bool) DirectoryLoader { func formatPath(fileName string, root string, pathFormat PathFormat) string { switch pathFormat { case SlashRooted: - if !strings.HasPrefix(fileName, string(filepath.Separator)) { - return string(filepath.Separator) + fileName - } - return fileName + return util.WithPrefix(fileName, string(filepath.Separator)) case Chrooted: // Trim off the root directory and return path as if chrooted result := strings.TrimPrefix(fileName, filepath.FromSlash(root)) @@ -206,10 +205,7 @@ func formatPath(fileName string, root string, pathFormat PathFormat) string { if root == "." && (filepath.Base(fileName) == ManifestExt || filepath.Base(fileName) == ManifestProtoExt) { result = fileName } - if !strings.HasPrefix(result, string(filepath.Separator)) { - result = string(filepath.Separator) + result - } - return result + return util.WithPrefix(result, string(filepath.Separator)) case Passthrough: fallthrough default: @@ -444,13 +440,13 @@ func (it *iterator) Next() (*storage.Update, error) { } f.path = p - f.raw = item.Value - it.files = append(it.files, f) } - sortFilePathAscend(it.files) + slices.SortFunc(it.files, func(a, b file) int { + return cmp.Compare(len(a.path), len(b.path)) + }) } // If done reading files then just return io.EOF @@ -487,12 +483,6 @@ func NewIterator(raw []Raw) storage.Iterator { return &it } -func sortFilePathAscend(files []file) { - sort.Slice(files, func(i, j int) bool { - return len(files[i].path) < len(files[j].path) - }) -} - func getdepth(path string, isDir bool) int { if isDir { cleanedPath := strings.Trim(filepath.ToSlash(path), "/") diff --git a/vendor/github.com/open-policy-agent/opa/v1/bundle/proto.go b/vendor/github.com/open-policy-agent/opa/v1/bundle/proto.go index 500be418d5..fb700aba32 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/bundle/proto.go +++ b/vendor/github.com/open-policy-agent/opa/v1/bundle/proto.go @@ -9,7 +9,6 @@ import ( "fmt" "net/url" - "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/structpb" "github.com/open-policy-agent/opa/v1/ast" @@ -26,11 +25,11 @@ func ManifestToProto(m *Manifest) (*pb.Manifest, error) { return nil, nil } out := &pb.Manifest{ - Revision: proto.String(m.Revision), + Revision: new(m.Revision), } if m.Roots != nil { out.Roots = append([]string(nil), (*m.Roots)...) - out.RootsSet = proto.Bool(true) + out.RootsSet = new(true) } if len(m.WasmResolvers) > 0 { out.Wasm = make([]*pb.WasmResolver, len(m.WasmResolvers)) @@ -43,7 +42,7 @@ func ManifestToProto(m *Manifest) (*pb.Manifest, error) { } } if m.RegoVersion != nil { - out.RegoVersion = proto.Int32(int32(*m.RegoVersion)) + out.RegoVersion = new(int32(*m.RegoVersion)) } if len(m.FileRegoVersions) > 0 { out.FileRegoVersions = make(map[string]int32, len(m.FileRegoVersions)) @@ -66,8 +65,8 @@ func wasmResolverToProto(w *WasmResolver) (*pb.WasmResolver, error) { return nil, nil } out := &pb.WasmResolver{ - Entrypoint: proto.String(w.Entrypoint), - Module: proto.String(w.Module), + Entrypoint: new(w.Entrypoint), + Module: new(w.Module), } if len(w.Annotations) > 0 { out.Annotations = make([]*pb.Annotations, len(w.Annotations)) @@ -87,10 +86,10 @@ func annotationsToProto(a *ast.Annotations) (*pb.Annotations, error) { return nil, nil } out := &pb.Annotations{ - Scope: proto.String(a.Scope), - Title: proto.String(a.Title), - Entrypoint: proto.Bool(a.Entrypoint), - Description: proto.String(a.Description), + Scope: new(a.Scope), + Title: new(a.Title), + Entrypoint: new(a.Entrypoint), + Description: new(a.Description), Organizations: append([]string(nil), a.Organizations...), } if len(a.RelatedResources) > 0 { @@ -143,8 +142,8 @@ func relatedResourceToProto(r *ast.RelatedResourceAnnotation) *pb.RelatedResourc return nil } return &pb.RelatedResourceAnnotation{ - Ref: proto.String(r.Ref.String()), - Description: proto.String(r.Description), + Ref: new(r.Ref.String()), + Description: new(r.Description), } } @@ -153,8 +152,8 @@ func authorToProto(a *ast.AuthorAnnotation) *pb.AuthorAnnotation { return nil } return &pb.AuthorAnnotation{ - Name: proto.String(a.Name), - Email: proto.String(a.Email), + Name: new(a.Name), + Email: new(a.Email), } } @@ -163,8 +162,8 @@ func schemaToProto(s *ast.SchemaAnnotation) (*pb.SchemaAnnotation, error) { return nil, nil } out := &pb.SchemaAnnotation{ - Path: proto.String(s.Path.String()), - Schema: proto.String(s.Schema.String()), + Path: new(s.Path.String()), + Schema: new(s.Schema.String()), } if s.Definition != nil { v, err := jsonNormalizeValue(*s.Definition) @@ -181,7 +180,7 @@ func compileToProto(c *ast.CompileAnnotation) *pb.CompileAnnotation { return nil } out := &pb.CompileAnnotation{ - MaskRule: proto.String(c.MaskRule.String()), + MaskRule: new(c.MaskRule.String()), } if len(c.Unknowns) > 0 { out.Unknowns = make([]string, len(c.Unknowns)) @@ -197,9 +196,9 @@ func locationToProto(l *location.Location) *pb.Location { return nil } return &pb.Location{ - File: proto.String(l.File), - Row: proto.Int32(int32(l.Row)), - Col: proto.Int32(int32(l.Col)), + File: new(l.File), + Row: new(int32(l.Row)), + Col: new(int32(l.Col)), } } diff --git a/vendor/github.com/open-policy-agent/opa/v1/bundle/store.go b/vendor/github.com/open-policy-agent/opa/v1/bundle/store.go index 1784c98f5f..c5726986c1 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/bundle/store.go +++ b/vendor/github.com/open-policy-agent/opa/v1/bundle/store.go @@ -12,7 +12,7 @@ import ( "fmt" "maps" "path/filepath" - "sort" + "slices" "strings" "sync" @@ -528,7 +528,7 @@ func activateBundles(opts *ActivateOpts) error { } // Compile the modules all at once to avoid having to re-do work. - remainingAndExtra := make(map[string]*ast.Module) + remainingAndExtra := make(map[string]*ast.Module, len(remaining)+len(opts.ExtraModules)) maps.Copy(remainingAndExtra, remaining) maps.Copy(remainingAndExtra, opts.ExtraModules) @@ -805,13 +805,13 @@ func erasePolicies(ctx context.Context, store storage.Store, txn storage.Transac if err != nil { return nil, nil, err } - path, err := module.Package.Path.Ptr() + path, err := storage.NewPathForRef(module.Package.Path) if err != nil { return nil, nil, err } deleted := false for root := range roots { - if RootPathsContain([]string{root}, path) { + if rootPathsContainSegments([]string{root}, path) { if err := store.DeletePolicy(ctx, txn, id); err != nil { return nil, nil, err } @@ -833,25 +833,16 @@ func erasePolicies(ctx context.Context, store storage.Store, txn storage.Transac func writeManifestToStore(opts *ActivateOpts, name string, manifest Manifest) error { // Always write manifests to the named location. If the plugin is in the older style config // then also write to the old legacy unnamed location. - if err := WriteManifestToStore(opts.Ctx, opts.Store, opts.Txn, name, manifest); err != nil { - return err - } - - if opts.legacy { - if err := LegacyWriteManifestToStore(opts.Ctx, opts.Store, opts.Txn, manifest); err != nil { - return err - } + err := WriteManifestToStore(opts.Ctx, opts.Store, opts.Txn, name, manifest) + if err == nil && opts.legacy { + err = LegacyWriteManifestToStore(opts.Ctx, opts.Store, opts.Txn, manifest) } - return nil + return err } func writeEtagToStore(opts *ActivateOpts, name, etag string) error { - if err := WriteEtagToStore(opts.Ctx, opts.Store, opts.Txn, name, etag); err != nil { - return err - } - - return nil + return WriteEtagToStore(opts.Ctx, opts.Store, opts.Txn, name, etag) } func writeModuleRegoVersionToStore(ctx context.Context, store storage.Store, txn storage.Transaction, b *Bundle, @@ -927,7 +918,7 @@ func writeDataAndModules(ctx context.Context, store storage.Store, txn storage.T if m := f.module; m != nil { // 'f.module.Path' contains the module's path as it relates to the bundle root, and can be used for looking up the rego-version. // 'f.Path' can differ, based on how the bundle reader was initialized. - if err := writeModuleRegoVersionToStore(ctx, store, txn, b, *m, p.String(), runtimeRegoVersion); err != nil { + if err := writeModuleRegoVersionToStore(ctx, store, txn, b, *m, p.PolicyID(), runtimeRegoVersion); err != nil { return err } } @@ -994,7 +985,7 @@ func compileModules(compiler *ast.Compiler, m metrics.Metrics, bundles map[strin return compiler.Errors } - if authorizationDecisionRef.Equal(ast.EmptyRef()) { + if authorizationDecisionRef.Equal(ast.InternedEmptyRefValue) { return nil } @@ -1163,11 +1154,11 @@ func hasRootsOverlap(ctx context.Context, store storage.Store, txn storage.Trans } // Sort the bundle roots list. - sort.Slice(entries, func(i, j int) bool { - if entries[i].canonical != entries[j].canonical { - return entries[i].canonical < entries[j].canonical + slices.SortFunc(entries, func(a, b rootEntry) int { + if c := strings.Compare(a.canonical, b.canonical); c != 0 { + return c } - return entries[i].bundle < entries[j].bundle + return strings.Compare(a.bundle, b.bundle) }) collidingBundles := map[string]bool{} @@ -1226,8 +1217,7 @@ func hasRootsOverlap(ctx context.Context, store storage.Store, txn storage.Trans // is allowed to declare overlapping roots in its own manifest. if sawCrossBundleConflict { collidingBundles[entries[d].bundle] = true - paths := []string{groupDisplay, entries[d].displayRoot()} - sort.Strings(paths) + paths := util.Sorted([]string{groupDisplay, entries[d].displayRoot()}) conflictSet[fmt.Sprintf("%s overlaps %s", paths[0], paths[1])] = true } } @@ -1314,11 +1304,7 @@ func LegacyWriteManifestToStore(ctx context.Context, store storage.Store, txn st // // Deprecated: Use WriteManifestToStore and named bundles instead. func LegacyEraseManifestFromStore(ctx context.Context, store storage.Store, txn storage.Transaction) error { - err := store.Write(ctx, txn, storage.RemoveOp, legacyManifestStoragePath, nil) - if err != nil { - return err - } - return nil + return store.Write(ctx, txn, storage.RemoveOp, legacyManifestStoragePath, nil) } // LegacyReadRevisionFromStore will read the bundle manifest revision from the older single (unnamed) bundle manifest location. diff --git a/vendor/github.com/open-policy-agent/opa/v1/bundle/v1pb/manifest.pb.go b/vendor/github.com/open-policy-agent/opa/v1/bundle/v1pb/manifest.pb.go index 018001f2d9..195e08eb00 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/bundle/v1pb/manifest.pb.go +++ b/vendor/github.com/open-policy-agent/opa/v1/bundle/v1pb/manifest.pb.go @@ -42,7 +42,7 @@ type Manifest struct { // Free-form metadata object. Modeled as `Struct` because the Go field // is `map[string]any`. Metadata *structpb.Struct `protobuf:"bytes,6,opt,name=metadata" json:"metadata,omitempty"` - // True iff `bundle.Manifest.Roots` was non-nil. `repeated string` can't + // True if `bundle.Manifest.Roots` was non-nil. `repeated string` can't // distinguish nil (default to [""]) from explicit-empty (owns no paths). RootsSet *bool `protobuf:"varint,7,opt,name=roots_set,json=rootsSet" json:"roots_set,omitempty"` unknownFields protoimpl.UnknownFields diff --git a/vendor/github.com/open-policy-agent/opa/v1/format/format.go b/vendor/github.com/open-policy-agent/opa/v1/format/format.go index 1f25938bae..860fb1a9eb 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/format/format.go +++ b/vendor/github.com/open-policy-agent/opa/v1/format/format.go @@ -10,12 +10,12 @@ import ( "errors" "fmt" "slices" - "sort" "strings" "unicode" "github.com/open-policy-agent/opa/internal/future" "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/ast/location" "github.com/open-policy-agent/opa/v1/types" "github.com/open-policy-agent/opa/v1/util" ) @@ -169,7 +169,7 @@ type fmtOpts struct { func (o fmtOpts) keywords() []string { if o.regoV1 { - return ast.KeywordsV1[:] + return append(ast.KeywordsV1[:], o.futureKeywords...) } kws := ast.KeywordsV0[:] return append(kws, o.futureKeywords...) @@ -206,9 +206,6 @@ func AstWithOpts(x any, opts Opts) ([]byte, error) { } o.allowKeywordsInRefs = capabilities.ContainsFeature(ast.FeatureKeywordsInRefs) - memberRef := ast.Member.Ref() - memberWithKeyRef := ast.MemberWithKey.Ref() - // Preprocess the AST. Set any required defaults and calculate // values required for printing the formatted output. ast.WalkNodes(x, func(x ast.Node) bool { @@ -222,12 +219,23 @@ func AstWithOpts(x any, opts Opts) ([]byte, error) { case *ast.Expr: switch { - case n.IsCall() && memberRef.Equal(n.Operator()) || memberWithKeyRef.Equal(n.Operator()): + case n.IsCall() && ast.Interned.Refs.Member.Equal(n.Operator()) || + ast.Interned.Refs.MemberWithKey.Equal(n.Operator()): extraFutureKeywordImports["in"] = struct{}{} case n.IsEvery(): extraFutureKeywordImports["every"] = struct{}{} case n.IsNot(): extraFutureKeywordImports["not"] = struct{}{} + case n.IsAnd(): + extraFutureKeywordImports["and"] = struct{}{} + case n.IsOr(): + extraFutureKeywordImports["or"] = struct{}{} + } + + if n.Negated && isLogicalExpr(n) { + // A negated logical expression is written parenthesized + // (`not (a or b)`), which requires the `not` keyword. + extraFutureKeywordImports["not"] = struct{}{} } case *ast.Import: @@ -409,6 +417,15 @@ func defaultLocation(x ast.Node) *ast.Location { } type writer struct { + // parenExpr, when set, is an expression whose terms must be wrapped in parens + // when written; consumed by the first writeExpr that sees it. Any `with` + // clauses stay outside the parens, as `(x | y with p as 1)` doesn't parse. + parenExpr *ast.Expr + + // parenTerm, when set, is a term that must be wrapped in parens when written; + // consumed by the first writeTermParens that sees it. + parenTerm *ast.Term + buf bytes.Buffer indent string @@ -442,29 +459,47 @@ func (w *writer) writeModule(module *ast.Module) error { }) visitor.Walk(module) - sort.Slice(comments, func(i, j int) bool { - l, err := locLess(comments[i], comments[j]) + slices.SortFunc(comments, func(a, b *ast.Comment) int { + al, bl, err := getLocs(a, b) if err != nil { w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, "%s", err.Error())) } - return l + return locCmp(al, bl) }) - sort.Slice(others, func(i, j int) bool { - l, err := locLess(others[i], others[j]) + slices.SortFunc(others, func(a, b any) int { + al, bl, err := getLocs(a, b) if err != nil { w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, "%s", err.Error())) } - return l + return locCmp(al, bl) }) comments = trimTrailingWhitespaceInComments(comments) + // Imports added by the formatter get an assigned line number, which can sort + // after a rule's. An import written after a rule has no effect. + var added []*ast.Import + if addedImportFollowsRule(others) { + others = slices.DeleteFunc(others, func(x any) bool { + imp, ok := x.(*ast.Import) + if !ok || !isAddedImport(imp) { + return false + } + added = append(added, imp) + return true + }) + } + var err error comments, err = w.writePackage(pkg, comments) if err != nil { return err } + comments, err = w.writeImports(added, comments) + if err != nil { + return err + } var imports []*ast.Import var rules []*ast.Rule for len(others) > 0 { @@ -532,7 +567,7 @@ func (w *writer) writeComments(comments []*ast.Comment) error { var inMetadataBlock bool for i := range comments { if i > 0 { - l, err := locCmp(comments[i], comments[i-1]) + l, err := locCmpOrError(comments[i], comments[i-1]) if err != nil { return err } @@ -584,6 +619,10 @@ func (w *writer) writeRules(rules []*ast.Rule, comments []*ast.Comment) ([]*ast. return comments, nil } +// groupableOneLiner reports whether rule is written on a single line, and so may +// be grouped with an adjacent rule instead of being followed by a blank line. +// These conditions must agree with the inline body branch of writeRule, which +// doesn't end the line after the closing brace of a multi-line body. func (w *writer) groupableOneLiner(rule *ast.Rule) bool { // Location required to determine if two rules are adjacent in the policy. // If not, we respect line breaks between rules. @@ -591,6 +630,18 @@ func (w *writer) groupableOneLiner(rule *ast.Rule) bool { return false } + // An else block is always written on a line of its own, so the rule spans + // multiple lines even when its own body is written inline. + if rule.Else != nil { + return false + } + + // A lone set term body keeps its enclosing braces, and so is written as a + // multi-line block. + if len(rule.Body) == 1 && isSetTerm(rule.Body[0]) { + return false + } + partialSetException := w.fmtOpts.contains || rule.Head.Value != nil return (w.fmtOpts.regoV1 || w.fmtOpts.ifs) && partialSetException @@ -637,15 +688,21 @@ func (w *writer) writeRule(rule *ast.Rule, isElse bool, comments []*ast.Comment) // this excludes partial sets UNLESS `contains` is used partialSetException := w.fmtOpts.contains || rule.Head.Value != nil - if (w.fmtOpts.regoV1 || w.fmtOpts.ifs) && partialSetException { + usesIf := (w.fmtOpts.regoV1 || w.fmtOpts.ifs) && partialSetException + + if usesIf { w.write(" if") if len(rule.Body) == 1 { // Keep `if ` on one line when the single body term sits on the // same line as the end of the head. Comparing against the head's // start row would wrongly expand the condition into a block whenever // the head value spans multiple lines (e.g. a multi-line call). - headEndRow := rule.Head.Location.Row + strings.Count(string(rule.Head.Location.Text), "\n") - if rule.Body[0].Location.Row == headEndRow { + // + // Additionally, a single set term must not be stripped of the outer body + // braces, as that would semantically change the inner set to a body: + // `p if { { x } }` -> p if { x } + headEndRow, _ := location.EndOf(rule.Head.Location.Row, rule.Head.Location.Col, rule.Head.Location.Text) + if rule.Body[0].Location.Row == headEndRow && !isSetTerm(rule.Body[0]) { w.write(" ") var err error comments, err = w.writeExpr(rule.Body[0], comments) @@ -670,6 +727,13 @@ func (w *writer) writeRule(rule *ast.Rule, isElse bool, comments []*ast.Comment) w.endLine() } + // A leading set union renders as `x | y`, which the parser reads as a + // comprehension at the brace of a `p if { ... }` body, so it is parenthesized. + // An `else` body has no such ambiguity: its braces always open a body. + if usesIf && !isElse { + w.markUnionLead(rule.Body[0]) + } + w.up() comments, err = w.writeBody(rule.Body, comments) @@ -816,7 +880,7 @@ func (w *writer) writeHead(head *ast.Head, isDefault bool, isExpandedConst bool, args = append(args, arg) } var err error - comments, err = w.writeIterable(args, head.Location, closingLoc(0, 0, '(', ')', head.Location), comments, w.listWriter()) + comments, err = w.writeIterable(args, head.Location, closingLoc(0, 0, '(', ')', head.Location), comments, w.listWriter(false)) w.write(")") if err != nil { return comments, err @@ -922,6 +986,11 @@ func (w *writer) writeBody(body ast.Body, comments []*ast.Comment) ([]*ast.Comme } func (w *writer) writeExpr(expr *ast.Expr, comments []*ast.Comment) ([]*ast.Comment, error) { + parenTerms := w.parenExpr == expr + if parenTerms { + w.parenExpr = nil + } + var err error comments, err = w.insertComments(comments, expr.Location) if err != nil { @@ -931,8 +1000,20 @@ func (w *writer) writeExpr(expr *ast.Expr, comments []*ast.Comment) ([]*ast.Comm w.startLine() } + // `not` binds tighter than `and`/`or`, so a negated logical expression is + // parenthesized. Only reachable through programmatically built ASTs; the + // parser represents `not (a or b)` as an *ast.Not. + negatedLogical := expr.Negated && isLogicalExpr(expr) + if expr.Negated { w.write("not ") + if negatedLogical { + w.write("(") + } + } + + if parenTerms { + w.write("(") } switch t := expr.Terms.(type) { @@ -951,6 +1032,11 @@ func (w *writer) writeExpr(expr *ast.Expr, comments []*ast.Comment) ([]*ast.Comm if err != nil { return nil, err } + case *ast.LogicalAnd, *ast.LogicalOr: + comments, err = w.writeLogical(expr, comments) + if err != nil { + return nil, err + } case []*ast.Term: comments, err = w.writeFunctionCall(expr, comments) if err != nil { @@ -963,6 +1049,14 @@ func (w *writer) writeExpr(expr *ast.Expr, comments []*ast.Comment) ([]*ast.Comm } } + if parenTerms { + w.write(")") + } + + if negatedLogical { + w.write(")") + } + if len(expr.With) == 0 { return comments, nil } @@ -1026,7 +1120,23 @@ func exprTermsEndRow(expr *ast.Expr) int { } } text = bytes.TrimRight(text, " \t\r\n") - return loc.Row + bytes.Count(text, []byte{'\n'}) + endRow, _ := location.EndOf(loc.Row, loc.Col, text) + return endRow +} + +// isSetTerm reports whether expr is a non-negated set term. +func isSetTerm(expr *ast.Expr) bool { + if expr.IsNegated() { + return false + } + + term, ok := expr.Terms.(*ast.Term) + if !ok { + return false + } + + _, ok = term.Value.(ast.Set) + return ok } func (w *writer) writeSomeDecl(decl *ast.SomeDecl, comments []*ast.Comment) ([]*ast.Comment, error) { @@ -1128,6 +1238,12 @@ func (w *writer) writeNot(not *ast.Not, loc *ast.Location, comments []*ast.Comme w.write("not ") if not.ExplicitBody || len(not.Body) > 1 { + // A leading set union renders as `x | y`, which the parser reads as a + // comprehension at the brace, so it is parenthesized. + if isUnionExpr(not.Body[0]) { + w.parenExpr = not.Body[0] + } + w.write("{") comments, err = w.writeComprehensionBody('{', '}', not.Body, loc, loc, comments) if err != nil { @@ -1136,23 +1252,290 @@ func (w *writer) writeNot(not *ast.Not, loc *ast.Location, comments []*ast.Comme } } - if len(not.Body) == 1 && - not.Body[0].Location.Row == loc.Row { + if last := not.Body[len(not.Body)-1]; last.Location != nil && last.Location.Row == loc.Row { w.write(" ") } w.write("}") } else { + parens := notBodyNeedsParens(not.Body[0]) + if parens { + w.write("(") + } + comments, err = w.writeExpr(not.Body[0], comments) if err != nil { if !errors.As(err, &unexpectedCommentError{}) { return nil, err } } + + if parens { + w.write(")") + } } return comments, nil } +// notBodyNeedsParens reports whether the sole expression of an implicit `not` +// body must be parenthesized to be read back as that same expression. Mirrors +// notBodyNeedsParens in the ast package. +func notBodyNeedsParens(expr *ast.Expr) bool { + // A `with` on a bare operand of `not` binds to the whole `not` expression. + if len(expr.With) > 0 { + return true + } + + // `not` binds tighter than `and`/`or`. + if isLogicalExpr(expr) { + return true + } + + // `not not x` doesn't parse: a nested negation must be parenthesized to be + // read back as a body. + if _, ok := expr.Terms.(*ast.Not); ok { + return true + } + + // A value that renders brace-led would be re-read as an explicit body. + return exprRendersBraceLead(expr) +} + +// logicalOperand is one operand of an `and`/`or` chain. +type logicalOperand struct { + body ast.Body + + // explicit is set for `{...}` operands, which scope their contents and are + // always written braced. + explicit bool + + // parens is set for implicit operands that must be parenthesized to be read + // back as the same expression. + parens bool + + // brace is the location of the operand's opening `{`, for explicit operands. + brace *ast.Location +} + +// logicalStep is one operator application of an `and`/`or` chain. +type logicalStep struct { + op string + rhs logicalOperand + + // lhsEndRow is the row on which everything to the left of the operator ends. + lhsEndRow int +} + +// breaksLine reports whether the rhs operand is written on a line of its own, +// i.e. starts on a later row than the end of everything left of the operator. +// An explicit operand starts at its opening brace, an implicit one at its sole +// expression; a missing location leaves the row unknown, so no break. +func (s logicalStep) breaksLine() bool { + var start *ast.Location + if s.rhs.explicit { + start = s.rhs.brace + } else if len(s.rhs.body) > 0 { + start = s.rhs.body[0].Location + } + + return start != nil && start.Row > s.lhsEndRow +} + +func (w *writer) writeLogical(expr *ast.Expr, comments []*ast.Comment) ([]*ast.Comment, error) { + lhs, steps := flattenLogical(expr) + + comments, err := w.writeLogicalOperand(lhs, comments) + if err != nil && !errors.As(err, &unexpectedCommentError{}) { + return comments, err + } + + var indented bool + + for _, s := range steps { + w.write(" " + s.op) + + if s.breaksLine() { + if !indented { + w.up() + defer w.down() //nolint:errcheck + indented = true + } + w.endLine() + w.startLine() + } else { + w.write(" ") + } + + comments, err = w.writeLogicalOperand(s.rhs, comments) + if err != nil && !errors.As(err, &unexpectedCommentError{}) { + return comments, err + } + } + + return comments, nil +} + +func (w *writer) writeLogicalOperand(o logicalOperand, comments []*ast.Comment) ([]*ast.Comment, error) { + if !o.explicit { + if o.parens { + w.write("(") + defer w.write(")") + } + + return w.writeExpr(o.body[0], comments) + } + + if len(o.body) == 0 { + w.write("{}") + return comments, nil + } + + // A leading set union renders as `x | y`, which the parser reads as a + // comprehension at the brace, so it is parenthesized. + if isUnionExpr(o.body[0]) { + w.parenExpr = o.body[0] + } + + w.write("{") + comments, err := w.writeComprehensionBody('{', '}', o.body, o.brace, o.brace, comments) + if err != nil { + if !errors.As(err, &unexpectedCommentError{}) { + return comments, err + } + } + + if last := o.body[len(o.body)-1]; last.Location != nil && last.Location.Row == o.brace.Row { + w.write(" ") + } + w.write("}") + + return comments, nil +} + +// flattenLogical returns the leading operand and the operator applications of an +// `and`/`or` chain. Chains are left-associative, so the operands of +// `a and b and c` -- And{And{a, b}, c} -- are collected into a single chain, +// written with one level of continuation indent. A nested node that requires +// parens stays an operand of its own. +func flattenLogical(expr *ast.Expr) (logicalOperand, []logicalStep) { + op, lhs, rhs, explicitLhs, explicitRhs := logicalParts(expr) + + step := logicalStep{ + op: op, + rhs: newLogicalOperand(rhs, explicitRhs, op, true, expr.Location), + } + + if !explicitLhs && len(lhs) == 1 && isLogicalExpr(lhs[0]) && !logicalOperandNeedsParens(lhs[0], op, false) { + step.lhsEndRow = bodyEndRow(lhs) + first, steps := flattenLogical(lhs[0]) + + return first, append(steps, step) + } + + first := newLogicalOperand(lhs, explicitLhs, op, false, expr.Location) + step.lhsEndRow = logicalOperandEndRow(first) + + return first, []logicalStep{step} +} + +func logicalParts(expr *ast.Expr) (op string, lhs, rhs ast.Body, explicitLhs, explicitRhs bool) { + switch t := expr.Terms.(type) { + case *ast.LogicalAnd: + return "and", t.Lhs, t.Rhs, t.ExplicitLhs, t.ExplicitRhs + case *ast.LogicalOr: + return "or", t.Lhs, t.Rhs, t.ExplicitLhs, t.ExplicitRhs + } + + return "", nil, nil, false, false +} + +func newLogicalOperand(b ast.Body, explicit bool, parentOp string, rhs bool, node *ast.Location) logicalOperand { + if explicit || len(b) != 1 { + return logicalOperand{body: b, explicit: true, brace: operandBraceLoc(node, b)} + } + + return logicalOperand{body: b, parens: logicalOperandNeedsParens(b[0], parentOp, rhs)} +} + +// logicalOperandNeedsParens reports whether an implicit operand of parentOp must +// be parenthesized to be read back as that same expression. Mirrors +// logicalOperandNeedsParens in the ast package. +func logicalOperandNeedsParens(expr *ast.Expr, parentOp string, rhs bool) bool { + // A `with` on a bare operand binds to the whole and/or expression. + if len(expr.With) > 0 { + return true + } + + switch expr.Terms.(type) { + case *ast.LogicalOr: + // `or` binds looser than `and`: always parenthesize under `and`; under + // `or`, parenthesize only the rhs to preserve right-nesting. + return parentOp == "and" || rhs + case *ast.LogicalAnd: + // `and` binds tighter: no parens under `or`; under `and`, parenthesize + // only the rhs to preserve right-nesting. + return parentOp == "and" && rhs + } + + // A value that renders brace-led would be re-read as an explicit body. + return exprRendersBraceLead(expr) +} + +func logicalOperandEndRow(o logicalOperand) int { + if o.explicit { + if row := closingLoc(0, 0, '{', '}', o.brace).Row; row > 0 { + return row + } + } + + return bodyEndRow(o.body) +} + +// bodyEndRow returns the row of the last source line occupied by b. +func bodyEndRow(b ast.Body) int { + if len(b) == 0 { + return 0 + } + + loc := b[len(b)-1].Location + if loc == nil { + return 0 + } + + return loc.Row + bytes.Count(bytes.TrimRight(loc.Text, " \t\r\n"), []byte{'\n'}) +} + +// operandBraceLoc returns the location of the `{` opening an explicit operand +// body, derived from the location of the enclosing and/or node. The node +// location is returned as-is if the brace can't be located, e.g. for default +// locations. +func operandBraceLoc(node *ast.Location, b ast.Body) *ast.Location { + if node == nil || len(b) == 0 || b[0].Location == nil { + return node + } + + i := min(b[0].Location.Offset-node.Offset, len(node.Text)) + + for i--; i >= 0; i-- { + if node.Text[i] != '{' { + continue + } + + cpy := *node + cpy.Row = node.Row + bytes.Count(node.Text[:i], []byte{'\n'}) + cpy.Offset = node.Offset + i + cpy.Text = node.Text[i:] + + return &cpy + } + + return node +} + +func isLogicalExpr(expr *ast.Expr) bool { + return expr.IsAnd() || expr.IsOr() +} + func (w *writer) writeFunctionCall(expr *ast.Expr, comments []*ast.Comment) ([]*ast.Comment, error) { terms := expr.Terms.([]*ast.Term) @@ -1214,13 +1597,10 @@ func (w *writer) writeFunctionCallPlain(terms []*ast.Term, comments []*ast.Comme w.write("(") defer w.write(")") - args := make([]any, len(terms)-1) - for i, t := range terms[1:] { - args[i] = t - } + args := util.ToSliceOfAny(terms[1:]) loc := terms[0].Location var err error - comments, err = w.writeIterable(args, loc, closingLoc(0, 0, '(', ')', loc), comments, w.listWriter()) + comments, err = w.writeIterable(args, loc, closingLoc(0, 0, '(', ')', loc), comments, w.listWriter(false)) if err != nil { return nil, err } @@ -1286,7 +1666,7 @@ func (w *writer) writeTerm(term *ast.Term, comments []*ast.Comment) ([]*ast.Comm // If beforeEnd refers to a comment within the source text range, clear it // This prevents the comment from being written twice if w.beforeEnd != nil && len(term.Location.Text) > 0 { - endRow := term.Location.Row + bytes.Count(term.Location.Text, []byte{'\n'}) + endRow, _ := location.EndOf(term.Location.Row, term.Location.Col, term.Location.Text) if w.beforeEnd.Location.Row >= term.Location.Row && w.beforeEnd.Location.Row <= endRow { w.beforeEnd = nil } @@ -1333,6 +1713,11 @@ func (w *writer) writeUnformatted(location *ast.Location, currentComments []*ast } func (w *writer) writeTermParens(parens bool, term *ast.Term, comments []*ast.Comment) ([]*ast.Comment, error) { + if w.parenTerm == term { + w.parenTerm = nil + parens = true + } + var err error comments, err = w.insertComments(comments, term.Location) if err != nil { @@ -1702,7 +2087,7 @@ func (w *writer) writeArray(arr *ast.Array, loc *ast.Location, comments []*ast.C s = append(s, t) }) var err error - comments, err = w.writeIterable(s, loc, closingLoc(0, 0, '[', ']', loc), comments, w.listWriter()) + comments, err = w.writeIterable(s, loc, closingLoc(0, 0, '[', ']', loc), comments, w.listWriter(true)) if err != nil { return nil, err } @@ -1729,7 +2114,7 @@ func (w *writer) writeSet(set ast.Set, loc *ast.Location, comments []*ast.Commen s = append(s, t) }) var err error - comments, err = w.writeIterable(s, loc, closingLoc(0, 0, '{', '}', loc), comments, w.listWriter()) + comments, err = w.writeIterable(s, loc, closingLoc(0, 0, '{', '}', loc), comments, w.listWriter(true)) if err != nil { return nil, err } @@ -1754,10 +2139,19 @@ func (w *writer) writeObjectComprehension(object *ast.ObjectComprehension, loc * w.write("{") defer w.write("}") - object.Value.Location = object.Key.Location // Ensure the value is not written on the next line. - if object.Key.Location.Row-loc.Row > 1 { - w.endLine() - w.startLine() + // Ensure the value is not written on the next line. writeComprehension + // breaks before the term whenever the term's row is below the row the + // comprehension opened on, so the value is given a location on that row + // rather than its own, which may already be a row further down. Copying + // the value's own location rather than the key's keeps Text intact, which + // writeComprehension reads to decide whether a call term was parenthesised. + valueLoc := *object.Value.Location + valueLoc.Row = loc.Row + object.Value.Location = &valueLoc + + paren := isUnionCall(object.Key) + if paren { + w.write("(") } var err error @@ -1765,6 +2159,10 @@ func (w *writer) writeObjectComprehension(object *ast.ObjectComprehension, loc * if err != nil { return nil, err } + if paren { + w.write(")") + } + w.write(": ") return w.writeComprehension('{', '}', object.Value, object.Body, loc, comments) } @@ -1776,9 +2174,8 @@ func (w *writer) writeComprehension(openChar, closeChar byte, term *ast.Term, bo } parens := false - _, ok := term.Value.(ast.Call) - if ok { - parens = term.Location.Text[0] == 40 // Starts with "(" + if _, ok := term.Value.(ast.Call); ok { + parens = isUnionCall(term) || term.Location.Text[0] == 40 // Starts with "(" } var err error comments, err = w.writeTermParens(parens, term, comments) @@ -1791,11 +2188,7 @@ func (w *writer) writeComprehension(openChar, closeChar byte, term *ast.Term, bo } func (w *writer) writeComprehensionBody(openChar, closeChar byte, body ast.Body, term, compr *ast.Location, comments []*ast.Comment) ([]*ast.Comment, error) { - exprs := make([]any, 0, len(body)) - for _, expr := range body { - exprs = append(exprs, expr) - } - lines, err := w.groupIterable(exprs, term) + lines, err := w.groupIterable(util.ToSliceOfAny(body), term) if err != nil { return nil, err } @@ -1992,15 +2385,117 @@ func (w *writer) writeIterableLine(elements []any, comments []*ast.Comment, fn e return fn(elements[i], comments) } +// isUnionExpr reports whether expr is a set-union call that renders as a bare +// `x | y`, which is comprehension syntax at an operand brace. +func isUnionExpr(expr *ast.Expr) bool { + terms, ok := expr.Terms.([]*ast.Term) + return ok && len(terms) == 3 && ast.Interned.Refs.Or.Equal(terms[0].Value) +} + +// markUnionLead parenthesizes the set union leading the rendering of expr, if +// there is one: a leading `x | y` reads as comprehension syntax at the brace of +// the body holding expr. The union is either the expression itself, or the +// leading operand of an infix call — one nested deeper is already parenthesized +// by writeCall. +func (w *writer) markUnionLead(expr *ast.Expr) { + if expr.Negated { + return + } + + if isLogicalExpr(expr) { + if lhs, _ := flattenLogical(expr); !lhs.explicit && !lhs.parens { + w.markUnionLead(lhs.body[0]) + } + + return + } + + if isUnionExpr(expr) { + w.parenExpr = expr + return + } + + terms, ok := expr.Terms.([]*ast.Term) + if !ok { + return + } + + // Infix calls render an operand first: the result for the assigned form + // (`z = x | y`), otherwise the lhs (`x | y == z`). + if bi, ok := ast.BuiltinMap[terms[0].Value.String()]; ok && bi.Infix != "" { + var lead *ast.Term + + switch len(terms) { + case bi.Decl.Arity() + 1: + lead = terms[1] + case bi.Decl.Arity() + 2: + lead = terms[len(terms)-1] + } + + if lead != nil && isUnionCall(lead) { + w.parenTerm = lead + } + } +} + +// exprRendersBraceLead reports whether expr renders starting with a `{`. Such an +// expression needs parens in an operand position, as bare braces there are read as +// an explicit body. Mirrors rendersWithLeadingBrace in the ast package. +func exprRendersBraceLead(expr *ast.Expr) bool { + switch t := expr.Terms.(type) { + case *ast.Term: + return termRendersBraceLead(t) + case []*ast.Term: + // Infix calls render an operand first: the result for the assigned form + // (`z = x | y`), otherwise the lhs (`{x} == y`). + if bi, ok := ast.BuiltinMap[t[0].Value.String()]; ok && bi.Infix != "" { + switch len(t) { + case bi.Decl.Arity() + 1: + return termRendersBraceLead(t[1]) + case bi.Decl.Arity() + 2: + return termRendersBraceLead(t[len(t)-1]) + } + } + } + + return false +} + +func termRendersBraceLead(t *ast.Term) bool { + switch v := t.Value.(type) { + case ast.Set: + // The empty set renders as `set()`. + return v.Len() > 0 + case ast.Object, *ast.SetComprehension, *ast.ObjectComprehension: + return true + case ast.Ref: + return len(v) > 0 && termRendersBraceLead(v[0]) + case ast.Call: + // An infix call renders an operand first, so a brace-led operand of a + // nested call leads the whole rendering: `{1, 2} & s == set()`. + if bi, ok := ast.BuiltinMap[v[0].Value.String()]; ok && bi.Infix != "" && + len(v) == bi.Decl.Arity()+1 { + return termRendersBraceLead(v[1]) + } + } + + return false +} + +// isUnionCall returns true if the term is a call to the union built-in, whose +// infix form (`|`) is comprehension syntax when used inside a collection +// literal or as a comprehension term, and must be parenthesized there. +func isUnionCall(t *ast.Term) bool { + call, ok := t.Value.(ast.Call) + return ok && ast.Interned.Refs.Or.Equal(call[0].Value) +} + func (w *writer) objectWriter() entryWriter { return func(x any, comments []*ast.Comment) ([]*ast.Comment, error) { entry := x.([2]*ast.Term) - call, isCall := entry[0].Value.(ast.Call) - - paren := false - if isCall && ast.Or.Ref().Equal(call[0].Value) && entry[0].Location.Text[0] == 40 { // Starts with "(" - paren = true + paren := isUnionCall(entry[0]) + if paren { w.write("(") } @@ -2015,8 +2510,7 @@ func (w *writer) objectWriter() entryWriter { w.write(": ") - call, isCall = entry[1].Value.(ast.Call) - if isCall && ast.Or.Ref().Equal(call[0].Value) && entry[1].Location.Text[0] == 40 { // Starts with "(" + if isUnionCall(entry[1]) { w.write("(") defer w.write(")") } @@ -2025,12 +2519,11 @@ func (w *writer) objectWriter() entryWriter { } } -func (w *writer) listWriter() entryWriter { +func (w *writer) listWriter(parenUnionCalls bool) entryWriter { return func(x any, comments []*ast.Comment) ([]*ast.Comment, error) { t, ok := x.(*ast.Term) - if ok { - call, isCall := t.Value.(ast.Call) - if isCall && ast.Or.Ref().Equal(call[0].Value) && t.Location.Text[0] == 40 { // Starts with "(" + if ok && isUnionCall(t) { + if parenUnionCalls || t.Location.Text[0] == 40 { // Starts with "(" w.write("(") defer w.write(")") } @@ -2074,7 +2567,7 @@ func (w *writer) groupIterable(elements []any, last *ast.Location) ([][]any, err } slices.SortFunc(elements, func(i, j any) int { - l, err := locCmp(i, j) + l, err := locCmpOrError(i, j) if err != nil { w.errs = append(w.errs, ast.NewError(ast.FormatErr, &ast.Location{}, "%s", err.Error())) } @@ -2220,34 +2713,27 @@ loop: return rules, others[i:] } -func locLess(a, b any) (bool, error) { - c, err := locCmp(a, b) - return c < 0, err -} - -func locCmp(a, b any) (int, error) { - al, err := getLoc(a) - if err != nil { - return 0, err - } - bl, err := getLoc(b) +func locCmpOrError(a, b any) (int, error) { + al, bl, err := getLocs(a, b) if err != nil { return 0, err } + return locCmp(al, bl), nil +} + +func locCmp(a, b *ast.Location) int { switch { - case al == nil && bl == nil: - return 0, nil - case al == nil: - return -1, nil - case bl == nil: - return 1, nil + case a == b: + return 0 + case a == nil: + return -1 + case b == nil: + return 1 } - - if cmp := al.Row - bl.Row; cmp != 0 { - return cmp, nil - + if cmp := a.Row - b.Row; cmp != 0 { + return cmp } - return al.Col - bl.Col, nil + return a.Col - b.Col } func getLoc(x any) (*ast.Location, error) { @@ -2263,6 +2749,12 @@ func getLoc(x any) (*ast.Location, error) { } } +func getLocs(a, b any) (*ast.Location, *ast.Location, error) { + al, err1 := getLoc(a) + bl, err2 := getLoc(b) + return al, bl, errors.Join(err1, err2) +} + var negativeRow = &ast.Location{Row: -1} func closingLoc(skipOpen, skipClose, openChar, closeChar byte, loc *ast.Location) *ast.Location { @@ -2483,6 +2975,32 @@ func isFutureKeywordsImport(imp *ast.Import) bool { return len(path) >= 2 && ast.FutureRootDocument.Equal(path[0]) } +func isAddedImport(imp *ast.Import) bool { + return imp.Loc() != nil && imp.Loc().File == defaultLocationFile +} + +// addedImportFollowsRule reports whether an import added by the formatter would +// be written at or after the first rule. +func addedImportFollowsRule(others []any) bool { + firstRule := -1 + for _, x := range others { + if r, ok := x.(*ast.Rule); ok && r.Loc() != nil { + if firstRule < 0 || r.Loc().Row < firstRule { + firstRule = r.Loc().Row + } + } + } + if firstRule < 0 { + return false + } + for _, x := range others { + if imp, ok := x.(*ast.Import); ok && isAddedImport(imp) && imp.Loc().Row >= firstRule { + return true + } + } + return false +} + func ensureRegoV1Import(imps []*ast.Import) []*ast.Import { return ensureImport(imps, ast.RegoV1CompatibleRef) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/ir/ir.go b/vendor/github.com/open-policy-agent/opa/v1/ir/ir.go index df6bbdcc1d..2e02fd6510 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ir/ir.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ir/ir.go @@ -15,15 +15,17 @@ package ir import ( "fmt" + "github.com/open-policy-agent/opa/v1/ast/location" "github.com/open-policy-agent/opa/v1/types" ) type ( // Policy represents a planned policy query. Policy struct { - Static *Static `json:"static,omitempty"` - Plans *Plans `json:"plans,omitempty"` - Funcs *Funcs `json:"funcs,omitempty"` + Static *Static `json:"static,omitempty"` + Plans *Plans `json:"plans,omitempty"` + Funcs *Funcs `json:"funcs,omitempty"` + UnplannedRules []*UnplannedRule `json:"unplanned_rules,omitempty"` } // Static represents a static data segment that is indexed into by the policy. @@ -84,7 +86,7 @@ type ( } locationStmt interface { - SetLocation(index, row, col int, file, text string) + SetLocation(index, row, col int, file string, text []byte) GetLocation() *Location } @@ -97,6 +99,15 @@ type ( StringConst struct { Value string `json:"value"` } + + // UnplannedRule represents a rule that was parsed but not included in the + // plan because it is not reachable from the entrypoint. + // This is used for coverage reporting, to distinguish rules that were never + // planned from rules that were planned but never executed. + UnplannedRule struct { + Path string `json:"path"` + Location *Location `json:"location"` + } ) const ( @@ -467,21 +478,45 @@ type ResultSetAddStmt struct { // Location records the filen index, and the row and column inside that file // that a statement can be connected to. type Location struct { - File int `json:"file"` // filename string constant index - Col int `json:"col"` - Row int `json:"row"` - file, text string // only used for debugging + File int `json:"file"` // filename string constant index + Col int `json:"col"` + Row int `json:"row"` + EndCol int `json:"end_col"` + EndRow int `json:"end_row"` + + // Text is only used for location ranges and debug prints. + // A named type is used so that its String method is called during printing. + // String cannot be set on Location since it is embedded and impacts parent + // structs if registered here. + Text locationText `json:"-"` + + file string // only used for debugging +} + +type locationText []byte + +func (d locationText) String() string { + return string(d) } // SetLocation sets the Location for a given Stmt. -func (l *Location) SetLocation(index, row, col int, file, text string) { +func (l *Location) SetLocation(index, row, col int, file string, text []byte) { *l = Location{ File: index, Row: row, Col: col, + Text: text, + file: file, - text: text, } + + l.EndRow, l.EndCol = location.EndOf(row, col, l.Text) +} + +// End returns the end row and col of the location range, expected to be called +// after SetLocation or unmarshalling. +func (l *Location) End() (row, col int) { + return l.EndRow, l.EndCol } // GetLocation returns a Stmt's Location. diff --git a/vendor/github.com/open-policy-agent/opa/v1/ir/marshal.go b/vendor/github.com/open-policy-agent/opa/v1/ir/marshal.go index f395fb2b62..6e13635f6c 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ir/marshal.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ir/marshal.go @@ -113,6 +113,8 @@ func (m *MakeNumberRefStmt) MarshalJSON() ([]byte, error) { File int `json:"file"` Col int `json:"col"` Row int `json:"row"` + EndCol int `json:"end_col"` + EndRow int `json:"end_row"` Index int `json:"index"` IndexLegacy int `json:"Index"` // deprecated; remove in next major Target Local `json:"target"` @@ -120,6 +122,8 @@ func (m *MakeNumberRefStmt) MarshalJSON() ([]byte, error) { File: m.File, Col: m.Col, Row: m.Row, + EndCol: m.EndCol, + EndRow: m.EndRow, Index: m.Index, IndexLegacy: m.Index, Target: m.Target, @@ -133,6 +137,8 @@ func (m *MakeNumberRefStmt) UnmarshalJSON(bs []byte) error { File int `json:"file"` Col int `json:"col"` Row int `json:"row"` + EndCol int `json:"end_col"` + EndRow int `json:"end_row"` Index *int `json:"index"` IndexLegacy *int `json:"Index"` Target Local `json:"target"` @@ -140,7 +146,7 @@ func (m *MakeNumberRefStmt) UnmarshalJSON(bs []byte) error { if err := json.Unmarshal(bs, &raw); err != nil { return err } - m.File, m.Col, m.Row, m.Target = raw.File, raw.Col, raw.Row, raw.Target + m.File, m.Col, m.Row, m.EndCol, m.EndRow, m.Target = raw.File, raw.Col, raw.Row, raw.EndCol, raw.EndRow, raw.Target switch { case raw.Index != nil: m.Index = *raw.Index diff --git a/vendor/github.com/open-policy-agent/opa/v1/ir/plan.proto b/vendor/github.com/open-policy-agent/opa/v1/ir/plan.proto index 9bd5541e8f..f1e5ba4342 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ir/plan.proto +++ b/vendor/github.com/open-policy-agent/opa/v1/ir/plan.proto @@ -14,6 +14,26 @@ message Policy { Static static = 1; Plans plans = 2; Funcs funcs = 3; + repeated UnplannedRule unplanned_rules = 4; +} + +// UnplannedRule mirrors `ir.UnplannedRule` in v1/ir/ir.go. +message UnplannedRule { + string path = 1; + Location location = 2; +} + +// Location mirrors `ir.Location` in v1/ir/ir.go. Note: Stmt inlines these +// same fields directly on its envelope (see the `Stmt` message below) +// rather than nesting a Location message, since every Stmt body embeds +// ir.Location anonymously; UnplannedRule references it as a named field +// instead, so it gets its own message here. +message Location { + int32 file = 1; + int32 col = 2; + int32 row = 3; + int32 end_col = 4; + int32 end_row = 5; } // Static mirrors `ir.Static` in v1/ir/ir.go. @@ -99,23 +119,26 @@ message Val { } // Stmt mirrors the `ir.Stmt` interface in v1/ir/ir.go. Every Stmt carries -// the source-location triple (file, col, row) on this envelope; the body -// messages below describe only the kind-specific payload. +// the source-location quintuple (file, col, row, end_col, end_row) on this +// envelope; the body messages below describe only the kind-specific +// payload. // // On the Go side, `ir.Location` is embedded into every concrete Stmt -// implementation, so `encoding/json` flattens File/Col/Row into the -// emitted JSON body. The proto promotes those fields to the envelope -// because that's both more idiomatic protobuf and lets every body +// implementation, so `encoding/json` flattens File/Col/Row/EndCol/EndRow +// into the emitted JSON body. The proto promotes those fields to the +// envelope because that's both more idiomatic protobuf and lets every body // message start its own field numbering at 1. // // Case-number assignments (4–37) are a stability commitment. Field -// numbers 1–3 are reserved for the location triple. New cases must be -// added with the next unused number; existing numbers must never be -// repurposed. +// numbers 1–3 and 38–39 are reserved for the location fields. New cases +// must be added with the next unused number; existing numbers must never +// be repurposed. message Stmt { int32 file = 1; int32 col = 2; int32 row = 3; + int32 end_col = 38; + int32 end_row = 39; oneof kind { ArrayAppendStmt array_append_stmt = 4; AssignIntStmt assign_int_stmt = 5; diff --git a/vendor/github.com/open-policy-agent/opa/v1/ir/plan.schema.json b/vendor/github.com/open-policy-agent/opa/v1/ir/plan.schema.json index e4af18ba23..5796706288 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ir/plan.schema.json +++ b/vendor/github.com/open-policy-agent/opa/v1/ir/plan.schema.json @@ -11,6 +11,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -27,6 +33,8 @@ "required": [ "array", "col", + "end_col", + "end_row", "file", "row", "value" @@ -39,6 +47,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -54,6 +68,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "row", "target", @@ -67,6 +83,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -82,6 +104,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "row", "source", @@ -95,6 +119,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -110,6 +140,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "row", "source", @@ -138,6 +170,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -157,6 +195,8 @@ "required": [ "blocks", "col", + "end_col", + "end_row", "file", "row" ], @@ -168,6 +208,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -180,6 +226,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "index", "row" @@ -212,6 +260,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -243,6 +297,8 @@ "required": [ "args", "col", + "end_col", + "end_row", "file", "path", "result", @@ -256,6 +312,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -281,6 +343,8 @@ "required": [ "args", "col", + "end_col", + "end_row", "file", "func", "result", @@ -294,6 +358,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -312,6 +382,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "key", "row", @@ -326,6 +398,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -343,6 +421,8 @@ "a", "b", "col", + "end_col", + "end_row", "file", "row" ], @@ -414,6 +494,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -426,6 +512,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "row", "source" @@ -438,6 +526,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -450,6 +544,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "row", "source" @@ -462,6 +558,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -474,6 +576,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "row", "source" @@ -486,6 +590,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -498,6 +608,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "row", "source" @@ -510,6 +622,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -522,6 +640,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "row", "source" @@ -534,6 +654,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -549,6 +675,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "row", "source", @@ -556,12 +684,46 @@ ], "additionalProperties": false }, + "Location": { + "type": "object", + "properties": { + "col": { + "type": "integer" + }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, + "file": { + "type": "integer" + }, + "row": { + "type": "integer" + } + }, + "required": [ + "col", + "end_col", + "end_row", + "file", + "row" + ], + "additionalProperties": false + }, "MakeArrayStmt": { "type": "object", "properties": { "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -578,6 +740,8 @@ "required": [ "capacity", "col", + "end_col", + "end_row", "file", "row", "target" @@ -590,6 +754,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -602,6 +772,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "row", "target" @@ -614,6 +786,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -629,6 +807,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "row", "target", @@ -648,6 +828,12 @@ "row": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "index": { "type": "integer" }, @@ -662,6 +848,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "index", "row", @@ -675,6 +863,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -687,6 +881,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "row", "target" @@ -699,6 +895,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -711,6 +913,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "row", "target" @@ -723,6 +927,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -732,6 +942,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "row" ], @@ -743,6 +955,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -760,6 +978,8 @@ "a", "b", "col", + "end_col", + "end_row", "file", "row" ], @@ -771,6 +991,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -791,6 +1017,8 @@ "required": [ "block", "col", + "end_col", + "end_row", "file", "row" ], @@ -802,6 +1030,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -820,6 +1054,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "key", "object", @@ -834,6 +1070,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -852,6 +1094,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "key", "object", @@ -866,6 +1110,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -886,6 +1136,8 @@ "a", "b", "col", + "end_col", + "end_row", "file", "row", "target" @@ -946,6 +1198,12 @@ }, "static": { "$ref": "#/$defs/Static" + }, + "unplanned_rules": { + "type": "array", + "items": { + "$ref": "#/$defs/UnplannedRule" + } } }, "additionalProperties": false @@ -956,6 +1214,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -968,6 +1232,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "row", "target" @@ -980,6 +1246,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -992,6 +1264,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "row", "value" @@ -1004,6 +1278,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -1016,6 +1296,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "row", "source" @@ -1028,6 +1310,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -1057,6 +1345,8 @@ "required": [ "block", "col", + "end_col", + "end_row", "file", "key", "row", @@ -1071,6 +1361,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -1086,6 +1382,8 @@ }, "required": [ "col", + "end_col", + "end_row", "file", "row", "set", @@ -1677,6 +1975,29 @@ ], "additionalProperties": false }, + "UnplannedRule": { + "type": "object", + "properties": { + "location": { + "oneOf": [ + { + "$ref": "#/$defs/Location" + }, + { + "type": "null" + } + ] + }, + "path": { + "type": "string" + } + }, + "required": [ + "location", + "path" + ], + "additionalProperties": false + }, "Val": { "oneOf": [ { @@ -1735,6 +2056,12 @@ "col": { "type": "integer" }, + "end_col": { + "type": "integer" + }, + "end_row": { + "type": "integer" + }, "file": { "type": "integer" }, @@ -1770,6 +2097,8 @@ "required": [ "block", "col", + "end_col", + "end_row", "file", "local", "path", diff --git a/vendor/github.com/open-policy-agent/opa/v1/ir/proto.go b/vendor/github.com/open-policy-agent/opa/v1/ir/proto.go index 0c89dcb105..222e668822 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ir/proto.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ir/proto.go @@ -8,8 +8,6 @@ import ( "fmt" "math" - "google.golang.org/protobuf/proto" - pb "github.com/open-policy-agent/opa/v1/ir/v1pb" ) @@ -58,14 +56,14 @@ func stringConstToProto(s *StringConst) *pb.StringConst { if s == nil { return nil } - return &pb.StringConst{Value: proto.String(s.Value)} + return &pb.StringConst{Value: new(s.Value)} } func builtinFuncToProto(b *BuiltinFunc) *pb.BuiltinFunc { if b == nil { return nil } - return &pb.BuiltinFunc{Name: proto.String(b.Name)} + return &pb.BuiltinFunc{Name: new(b.Name)} } func plansToProto(p *Plans) *pb.Plans { @@ -83,7 +81,7 @@ func planToProto(p *Plan) *pb.Plan { if p == nil { return nil } - out := &pb.Plan{Name: proto.String(p.Name), Blocks: make([]*pb.Block, len(p.Blocks))} + out := &pb.Plan{Name: new(p.Name), Blocks: make([]*pb.Block, len(p.Blocks))} for i, b := range p.Blocks { out.Blocks[i] = blockToProto(b) } @@ -106,9 +104,9 @@ func funcToProto(f *Func) *pb.Func { return nil } out := &pb.Func{ - Name: proto.String(f.Name), + Name: new(f.Name), Params: localsToInt32s(f.Params), - Result: proto.Int32(toInt32(f.Return)), + Result: new(toInt32(f.Return)), Blocks: make([]*pb.Block, len(f.Blocks)), Path: f.Path, } @@ -189,30 +187,32 @@ func stmtToProto(s Stmt) *pb.Stmt { } loc := s.GetLocation() out := &pb.Stmt{ - File: proto.Int32(toInt32(loc.File)), - Col: proto.Int32(toInt32(loc.Col)), - Row: proto.Int32(toInt32(loc.Row)), + File: new(toInt32(loc.File)), + Col: new(toInt32(loc.Col)), + Row: new(toInt32(loc.Row)), + EndCol: new(toInt32(loc.EndCol)), + EndRow: new(toInt32(loc.EndRow)), } switch x := s.(type) { case *ArrayAppendStmt: out.Kind = &pb.Stmt_ArrayAppendStmt{ArrayAppendStmt: &pb.ArrayAppendStmt{ Value: operandToProto(x.Value), - Array: proto.Int32(toInt32(x.Array)), + Array: new(toInt32(x.Array)), }} case *AssignIntStmt: out.Kind = &pb.Stmt_AssignIntStmt{AssignIntStmt: &pb.AssignIntStmt{ - Value: proto.Int64(x.Value), - Target: proto.Int32(toInt32(x.Target)), + Value: new(x.Value), + Target: new(toInt32(x.Target)), }} case *AssignVarOnceStmt: out.Kind = &pb.Stmt_AssignVarOnceStmt{AssignVarOnceStmt: &pb.AssignVarOnceStmt{ Source: operandToProto(x.Source), - Target: proto.Int32(toInt32(x.Target)), + Target: new(toInt32(x.Target)), }} case *AssignVarStmt: out.Kind = &pb.Stmt_AssignVarStmt{AssignVarStmt: &pb.AssignVarStmt{ Source: operandToProto(x.Source), - Target: proto.Int32(toInt32(x.Target)), + Target: new(toInt32(x.Target)), }} case *BlockStmt: body := &pb.BlockStmt{Blocks: make([]*pb.Block, len(x.Blocks))} @@ -221,24 +221,24 @@ func stmtToProto(s Stmt) *pb.Stmt { } out.Kind = &pb.Stmt_BlockStmt{BlockStmt: body} case *BreakStmt: - out.Kind = &pb.Stmt_BreakStmt{BreakStmt: &pb.BreakStmt{Index: proto.Uint32(x.Index)}} + out.Kind = &pb.Stmt_BreakStmt{BreakStmt: &pb.BreakStmt{Index: new(x.Index)}} case *CallDynamicStmt: out.Kind = &pb.Stmt_CallDynamicStmt{CallDynamicStmt: &pb.CallDynamicStmt{ Args: localsToInt32s(x.Args), - Result: proto.Int32(toInt32(x.Result)), + Result: new(toInt32(x.Result)), Path: operandsToProto(x.Path), }} case *CallStmt: out.Kind = &pb.Stmt_CallStmt{CallStmt: &pb.CallStmt{ - Function: proto.String(x.Func), + Function: new(x.Func), Args: operandsToProto(x.Args), - Result: proto.Int32(toInt32(x.Result)), + Result: new(toInt32(x.Result)), }} case *DotStmt: out.Kind = &pb.Stmt_DotStmt{DotStmt: &pb.DotStmt{ Source: operandToProto(x.Source), Key: operandToProto(x.Key), - Target: proto.Int32(toInt32(x.Target)), + Target: new(toInt32(x.Target)), }} case *EqualStmt: out.Kind = &pb.Stmt_EqualStmt{EqualStmt: &pb.EqualStmt{ @@ -248,39 +248,39 @@ func stmtToProto(s Stmt) *pb.Stmt { case *IsArrayStmt: out.Kind = &pb.Stmt_IsArrayStmt{IsArrayStmt: &pb.IsArrayStmt{Source: operandToProto(x.Source)}} case *IsDefinedStmt: - out.Kind = &pb.Stmt_IsDefinedStmt{IsDefinedStmt: &pb.IsDefinedStmt{Source: proto.Int32(toInt32(x.Source))}} + out.Kind = &pb.Stmt_IsDefinedStmt{IsDefinedStmt: &pb.IsDefinedStmt{Source: new(toInt32(x.Source))}} case *IsObjectStmt: out.Kind = &pb.Stmt_IsObjectStmt{IsObjectStmt: &pb.IsObjectStmt{Source: operandToProto(x.Source)}} case *IsSetStmt: out.Kind = &pb.Stmt_IsSetStmt{IsSetStmt: &pb.IsSetStmt{Source: operandToProto(x.Source)}} case *IsUndefinedStmt: - out.Kind = &pb.Stmt_IsUndefinedStmt{IsUndefinedStmt: &pb.IsUndefinedStmt{Source: proto.Int32(toInt32(x.Source))}} + out.Kind = &pb.Stmt_IsUndefinedStmt{IsUndefinedStmt: &pb.IsUndefinedStmt{Source: new(toInt32(x.Source))}} case *LenStmt: out.Kind = &pb.Stmt_LenStmt{LenStmt: &pb.LenStmt{ Source: operandToProto(x.Source), - Target: proto.Int32(toInt32(x.Target)), + Target: new(toInt32(x.Target)), }} case *MakeArrayStmt: out.Kind = &pb.Stmt_MakeArrayStmt{MakeArrayStmt: &pb.MakeArrayStmt{ - Capacity: proto.Int32(x.Capacity), - Target: proto.Int32(toInt32(x.Target)), + Capacity: new(x.Capacity), + Target: new(toInt32(x.Target)), }} case *MakeNullStmt: - out.Kind = &pb.Stmt_MakeNullStmt{MakeNullStmt: &pb.MakeNullStmt{Target: proto.Int32(toInt32(x.Target))}} + out.Kind = &pb.Stmt_MakeNullStmt{MakeNullStmt: &pb.MakeNullStmt{Target: new(toInt32(x.Target))}} case *MakeNumberIntStmt: out.Kind = &pb.Stmt_MakeNumberIntStmt{MakeNumberIntStmt: &pb.MakeNumberIntStmt{ - Value: proto.Int64(x.Value), - Target: proto.Int32(toInt32(x.Target)), + Value: new(x.Value), + Target: new(toInt32(x.Target)), }} case *MakeNumberRefStmt: out.Kind = &pb.Stmt_MakeNumberRefStmt{MakeNumberRefStmt: &pb.MakeNumberRefStmt{ - Index: proto.Int32(toInt32(x.Index)), - Target: proto.Int32(toInt32(x.Target)), + Index: new(toInt32(x.Index)), + Target: new(toInt32(x.Target)), }} case *MakeObjectStmt: - out.Kind = &pb.Stmt_MakeObjectStmt{MakeObjectStmt: &pb.MakeObjectStmt{Target: proto.Int32(toInt32(x.Target))}} + out.Kind = &pb.Stmt_MakeObjectStmt{MakeObjectStmt: &pb.MakeObjectStmt{Target: new(toInt32(x.Target))}} case *MakeSetStmt: - out.Kind = &pb.Stmt_MakeSetStmt{MakeSetStmt: &pb.MakeSetStmt{Target: proto.Int32(toInt32(x.Target))}} + out.Kind = &pb.Stmt_MakeSetStmt{MakeSetStmt: &pb.MakeSetStmt{Target: new(toInt32(x.Target))}} case *NopStmt: out.Kind = &pb.Stmt_NopStmt{NopStmt: &pb.NopStmt{}} case *NotEqualStmt: @@ -294,41 +294,41 @@ func stmtToProto(s Stmt) *pb.Stmt { out.Kind = &pb.Stmt_ObjectInsertOnceStmt{ObjectInsertOnceStmt: &pb.ObjectInsertOnceStmt{ Key: operandToProto(x.Key), Value: operandToProto(x.Value), - Object: proto.Int32(toInt32(x.Object)), + Object: new(toInt32(x.Object)), }} case *ObjectInsertStmt: out.Kind = &pb.Stmt_ObjectInsertStmt{ObjectInsertStmt: &pb.ObjectInsertStmt{ Key: operandToProto(x.Key), Value: operandToProto(x.Value), - Object: proto.Int32(toInt32(x.Object)), + Object: new(toInt32(x.Object)), }} case *ObjectMergeStmt: out.Kind = &pb.Stmt_ObjectMergeStmt{ObjectMergeStmt: &pb.ObjectMergeStmt{ - A: proto.Int32(toInt32(x.A)), - B: proto.Int32(toInt32(x.B)), - Target: proto.Int32(toInt32(x.Target)), + A: new(toInt32(x.A)), + B: new(toInt32(x.B)), + Target: new(toInt32(x.Target)), }} case *ResetLocalStmt: - out.Kind = &pb.Stmt_ResetLocalStmt{ResetLocalStmt: &pb.ResetLocalStmt{Target: proto.Int32(toInt32(x.Target))}} + out.Kind = &pb.Stmt_ResetLocalStmt{ResetLocalStmt: &pb.ResetLocalStmt{Target: new(toInt32(x.Target))}} case *ResultSetAddStmt: - out.Kind = &pb.Stmt_ResultSetAddStmt{ResultSetAddStmt: &pb.ResultSetAddStmt{Value: proto.Int32(toInt32(x.Value))}} + out.Kind = &pb.Stmt_ResultSetAddStmt{ResultSetAddStmt: &pb.ResultSetAddStmt{Value: new(toInt32(x.Value))}} case *ReturnLocalStmt: - out.Kind = &pb.Stmt_ReturnLocalStmt{ReturnLocalStmt: &pb.ReturnLocalStmt{Source: proto.Int32(toInt32(x.Source))}} + out.Kind = &pb.Stmt_ReturnLocalStmt{ReturnLocalStmt: &pb.ReturnLocalStmt{Source: new(toInt32(x.Source))}} case *ScanStmt: out.Kind = &pb.Stmt_ScanStmt{ScanStmt: &pb.ScanStmt{ - Source: proto.Int32(toInt32(x.Source)), - Key: proto.Int32(toInt32(x.Key)), - Value: proto.Int32(toInt32(x.Value)), + Source: new(toInt32(x.Source)), + Key: new(toInt32(x.Key)), + Value: new(toInt32(x.Value)), Block: blockToProto(x.Block), }} case *SetAddStmt: out.Kind = &pb.Stmt_SetAddStmt{SetAddStmt: &pb.SetAddStmt{ Value: operandToProto(x.Value), - Set: proto.Int32(toInt32(x.Set)), + Set: new(toInt32(x.Set)), }} case *WithStmt: out.Kind = &pb.Stmt_WithStmt{WithStmt: &pb.WithStmt{ - Local: proto.Int32(toInt32(x.Local)), + Local: new(toInt32(x.Local)), Path: intsToInt32s(x.Path), Value: operandToProto(x.Value), Block: blockToProto(x.Block), diff --git a/vendor/github.com/open-policy-agent/opa/v1/ir/v1pb/plan.pb.go b/vendor/github.com/open-policy-agent/opa/v1/ir/v1pb/plan.pb.go index a47afbcc0f..fe55e67fc0 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/ir/v1pb/plan.pb.go +++ b/vendor/github.com/open-policy-agent/opa/v1/ir/v1pb/plan.pb.go @@ -668,24 +668,27 @@ func (*Val_Local) isVal_Kind() {} func (*Val_StringIndex) isVal_Kind() {} // Stmt mirrors the `ir.Stmt` interface in v1/ir/ir.go. Every Stmt carries -// the source-location triple (file, col, row) on this envelope; the body -// messages below describe only the kind-specific payload. +// the source-location quintuple (file, col, row, end_col, end_row) on this +// envelope; the body messages below describe only the kind-specific +// payload. // // On the Go side, `ir.Location` is embedded into every concrete Stmt -// implementation, so `encoding/json` flattens File/Col/Row into the -// emitted JSON body. The proto promotes those fields to the envelope -// because that's both more idiomatic protobuf and lets every body +// implementation, so `encoding/json` flattens File/Col/Row/EndCol/EndRow +// into the emitted JSON body. The proto promotes those fields to the +// envelope because that's both more idiomatic protobuf and lets every body // message start its own field numbering at 1. // // Case-number assignments (4–37) are a stability commitment. Field -// numbers 1–3 are reserved for the location triple. New cases must be -// added with the next unused number; existing numbers must never be -// repurposed. +// numbers 1–3 and 38–39 are reserved for the location fields. New cases +// must be added with the next unused number; existing numbers must never +// be repurposed. type Stmt struct { - state protoimpl.MessageState `protogen:"open.v1"` - File *int32 `protobuf:"varint,1,opt,name=file" json:"file,omitempty"` - Col *int32 `protobuf:"varint,2,opt,name=col" json:"col,omitempty"` - Row *int32 `protobuf:"varint,3,opt,name=row" json:"row,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + File *int32 `protobuf:"varint,1,opt,name=file" json:"file,omitempty"` + Col *int32 `protobuf:"varint,2,opt,name=col" json:"col,omitempty"` + Row *int32 `protobuf:"varint,3,opt,name=row" json:"row,omitempty"` + EndCol *int32 `protobuf:"varint,38,opt,name=end_col,json=endCol" json:"end_col,omitempty"` + EndRow *int32 `protobuf:"varint,39,opt,name=end_row,json=endRow" json:"end_row,omitempty"` // Types that are valid to be assigned to Kind: // // *Stmt_ArrayAppendStmt @@ -778,6 +781,20 @@ func (x *Stmt) GetRow() int32 { return 0 } +func (x *Stmt) GetEndCol() int32 { + if x != nil && x.EndCol != nil { + return *x.EndCol + } + return 0 +} + +func (x *Stmt) GetEndRow() int32 { + if x != nil && x.EndRow != nil { + return *x.EndRow + } + return 0 +} + func (x *Stmt) GetKind() isStmt_Kind { if x != nil { return x.Kind @@ -3100,11 +3117,13 @@ const file_v1_ir_plan_proto_rawDesc = "" + "\x04bool\x18\x01 \x01(\bH\x00R\x04bool\x12\x16\n" + "\x05local\x18\x02 \x01(\x05H\x00R\x05local\x12#\n" + "\fstring_index\x18\x03 \x01(\x05H\x00R\vstringIndexB\x06\n" + - "\x04kind\"\xf5\x11\n" + + "\x04kind\"\xa7\x12\n" + "\x04Stmt\x12\x12\n" + "\x04file\x18\x01 \x01(\x05R\x04file\x12\x10\n" + "\x03col\x18\x02 \x01(\x05R\x03col\x12\x10\n" + - "\x03row\x18\x03 \x01(\x05R\x03row\x12H\n" + + "\x03row\x18\x03 \x01(\x05R\x03row\x12\x17\n" + + "\aend_col\x18& \x01(\x05R\x06endCol\x12\x17\n" + + "\aend_row\x18' \x01(\x05R\x06endRow\x12H\n" + "\x11array_append_stmt\x18\x04 \x01(\v2\x1a.opa.ir.v1.ArrayAppendStmtH\x00R\x0farrayAppendStmt\x12B\n" + "\x0fassign_int_stmt\x18\x05 \x01(\v2\x18.opa.ir.v1.AssignIntStmtH\x00R\rassignIntStmt\x12O\n" + "\x14assign_var_once_stmt\x18\x06 \x01(\v2\x1c.opa.ir.v1.AssignVarOnceStmtH\x00R\x11assignVarOnceStmt\x12B\n" + diff --git a/vendor/github.com/open-policy-agent/opa/v1/loader/loader.go b/vendor/github.com/open-policy-agent/opa/v1/loader/loader.go index d97e3e5409..f78f9fb1d2 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/loader/loader.go +++ b/vendor/github.com/open-policy-agent/opa/v1/loader/loader.go @@ -12,6 +12,7 @@ import ( "io/fs" "os" "path/filepath" + "runtime" "strings" "sigs.k8s.io/yaml" @@ -29,6 +30,9 @@ import ( "github.com/open-policy-agent/opa/v1/util" ) +// goos is overridden in tests to exercise Windows path handling on other platforms. +var goos = runtime.GOOS + // Result represents the result of successfully loading zero or more files. type Result struct { Documents map[string]any @@ -592,6 +596,11 @@ func SplitPrefix(path string) ([]string, string) { if strings.Index(path, "://") == strings.Index(path, ":") { return nil, path } + // On Windows, a leading colon can belong to the path itself, separating the + // volume name from the rest of the path, rather than to a data prefix. + if hasWindowsVolumeName(path) { + return nil, path + } parts := strings.SplitN(path, ":", 2) if len(parts) == 2 && len(parts[0]) > 0 { return strings.Split(parts[0], "."), parts[1] @@ -599,6 +608,25 @@ func SplitPrefix(path string) ([]string, string) { return nil, path } +// hasWindowsVolumeName returns true on Windows if path begins with a volume +// name, i.e. a drive letter followed by a colon and a separator (c:/foo) or a +// UNC/device prefix (\\?\c:\foo), but not a drive-relative path (c:foo), which +// is read as a single-character data prefix instead. +func hasWindowsVolumeName(path string) bool { + if goos != "windows" || len(path) < 3 { + return false + } + // UNC and device paths, e.g. \\server\share or \\?\c:\foo. These aren't all + // loadable -- UNC reads are rejected outright -- but they're never prefixes, + // and splitting them would hide the path from that check. + if isSlash(path[0]) && isSlash(path[1]) { + return true + } + // Drive-rooted paths, e.g. c:/foo. + c := path[0] + return ('a' <= c && c <= 'z' || 'A' <= c && c <= 'Z') && path[1] == ':' && isSlash(path[2]) +} + func (l *Result) merge(path string, result any) error { switch result := result.(type) { case bundle.Bundle: diff --git a/vendor/github.com/open-policy-agent/opa/v1/logging/logging.go b/vendor/github.com/open-policy-agent/opa/v1/logging/logging.go index 135785994f..03d5f33c64 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/logging/logging.go +++ b/vendor/github.com/open-policy-agent/opa/v1/logging/logging.go @@ -336,15 +336,15 @@ func (h *SlogHandler) Handle(ctx context.Context, record slog.Record) error { msg := record.Message switch record.Level { case slog.LevelDebug: - logger.Debug(msg) + logger.Debug("%s", msg) case slog.LevelInfo: - logger.Info(msg) + logger.Info("%s", msg) case slog.LevelWarn: - logger.Warn(msg) + logger.Warn("%s", msg) case slog.LevelError: - logger.Error(msg) + logger.Error("%s", msg) default: - logger.Info(msg) + logger.Info("%s", msg) } return nil } diff --git a/vendor/github.com/open-policy-agent/opa/v1/metrics/metrics.go b/vendor/github.com/open-policy-agent/opa/v1/metrics/metrics.go index 481f27337e..119af78aab 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/metrics/metrics.go +++ b/vendor/github.com/open-policy-agent/opa/v1/metrics/metrics.go @@ -311,19 +311,19 @@ type Counter interface { } type counter struct { - c uint64 + c atomic.Uint64 } func (c *counter) Incr() { - atomic.AddUint64(&c.c, 1) + c.c.Add(1) } func (c *counter) Add(n uint64) { - atomic.AddUint64(&c.c, n) + c.c.Add(n) } func (c *counter) Value() any { - return atomic.LoadUint64(&c.c) + return c.c.Load() } func Statistics(num ...int64) any { diff --git a/vendor/github.com/open-policy-agent/opa/v1/rego/rego.go b/vendor/github.com/open-policy-agent/opa/v1/rego/rego.go index 097b80e3d8..fa7d5ed349 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/rego/rego.go +++ b/vendor/github.com/open-policy-agent/opa/v1/rego/rego.go @@ -122,6 +122,7 @@ type EvalContext struct { printHook print.Hook capabilities *ast.Capabilities strictBuiltinErrors bool + builtinErrorList *[]topdown.Error virtualCache topdown.VirtualCache baseCache topdown.BaseCache tracing tracing.Options @@ -388,6 +389,15 @@ func EvalPrintHook(ph print.Hook) EvalOption { } } +// EvalBuiltinErrorList overrides, for this Eval call only, the list +// built-in errors are appended to — letting a caller that evaluates the +// same PreparedEvalQuery multiple times keep each call's errors separate. +func EvalBuiltinErrorList(list *[]topdown.Error) EvalOption { + return func(e *EvalContext) { + e.builtinErrorList = list + } +} + // 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 { @@ -447,8 +457,12 @@ func EvalEvaluatedRuleTracker(t *topdown.EvaluatedRuleTracker) EvalOption { } func (pq preparedQuery) Modules() map[string]*ast.Module { - mods := make(map[string]*ast.Module) + size := len(pq.r.parsedModules) + for _, b := range pq.r.bundles { + size += len(b.Modules) + } + mods := make(map[string]*ast.Module, size) maps.Copy(mods, pq.r.parsedModules) for _, b := range pq.r.bundles { @@ -485,6 +499,7 @@ func (pq preparedQuery) newEvalContext(ctx context.Context, options []EvalOption printHook: pq.r.printHook, capabilities: pq.r.capabilities, strictBuiltinErrors: pq.r.strictBuiltinErrors, + builtinErrorList: pq.r.builtinErrorList, tracing: pq.r.distributedTracingOpts, } @@ -492,9 +507,7 @@ func (pq preparedQuery) newEvalContext(ctx context.Context, options []EvalOption o(ectx) } - if ectx.metrics == nil { - ectx.metrics = metrics.New() - } + ectx.metrics = util.Or(ectx.metrics, metrics.New) if ectx.instrument { ectx.instrumentation = topdown.NewInstrumentation(ectx.metrics) @@ -527,11 +540,9 @@ func (pq preparedQuery) newEvalContext(ctx context.Context, options []EvalOption } if ectx.parsedInput == nil { - if ectx.rawInput == nil { - // Fall back to the original Rego objects input if none was specified - // Note that it could still be nil - ectx.rawInput = pq.r.rawInput - } + // Fall back to the original Rego objects input if none was specified + // Note that it could still be nil + ectx.rawInput = util.NilOr(ectx.rawInput, pq.r.rawInput) if pq.r.targetPlugin(pq.r.target) == nil && // no plugin claims this target pq.r.target != targetWasm { @@ -597,13 +608,16 @@ func (errs Errors) Error() string { return "no error" } if len(errs) == 1 { - return fmt.Sprintf("1 error occurred: %v", errs[0].Error()) + return "1 error occurred: " + errs[0].Error() } - buf := []string{fmt.Sprintf("%v errors occurred", len(errs))} + bb := new(bytes.Buffer) + util.WriteInt(bb, len(errs)) + bb.WriteString(" errors occurred") for _, err := range errs { - buf = append(buf, err.Error()) + bb.WriteByte('\n') + bb.WriteString(err.Error()) } - return strings.Join(buf, "\n") + return bb.String() } var errPartialEvaluationNotEffective = errors.New("partial evaluation not effective") @@ -1461,9 +1475,7 @@ func New(options ...func(r *Rego)) *Rego { r.ownStore = false } - if r.metrics == nil { - r.metrics = metrics.New() - } + r.metrics = util.Or(r.metrics, metrics.New) if r.instrument { r.instrumentation = topdown.NewInstrumentation(r.metrics) @@ -1639,7 +1651,6 @@ func CompilePartial(yes bool) CompileOption { // Compile returns a compiled policy query. func (r *Rego) Compile(ctx context.Context, opts ...CompileOption) (*CompileResult, error) { var cfg CompileContext - for _, opt := range opts { opt(&cfg) } @@ -1648,7 +1659,6 @@ func (r *Rego) Compile(ctx context.Context, opts ...CompileOption) (*CompileResu modules := make([]*ast.Module, 0, len(r.compiler.Modules)) if cfg.partial { - pq, err := r.Partial(ctx) if err != nil { return nil, err @@ -1856,14 +1866,12 @@ func (r *Rego) PrepareForEval(ctx context.Context, opts ...PrepareOption) (Prepa return PreparedEvalQuery{}, err } - // nolint: staticcheck // SA4006 false positive cr, err := r.compileWasm(modules, queries, evalQueryType) if err != nil { _ = txnClose(ctx, err) // Ignore error return PreparedEvalQuery{}, err } - // nolint: staticcheck // SA4006 false positive data, err := r.store.Read(ctx, r.txn, storage.RootPath) if err != nil { _ = txnClose(ctx, err) // Ignore error @@ -1942,33 +1950,27 @@ func (r *Rego) PrepareForPartial(ctx context.Context, opts ...PrepareOption) (Pr return PreparedPartialQuery{preparedQuery{r, pCfg}}, err } -func (r *Rego) prepare(ctx context.Context, qType queryType, extras []extraStage) error { - var err error - +func (r *Rego) prepare(ctx context.Context, qType queryType, extras []extraStage) (err error) { r.parsedInput, err = r.parseInput() if err != nil { return err } - err = r.loadFiles(ctx, r.txn, r.metrics) - if err != nil { + if err := r.loadFiles(ctx, r.txn, r.metrics); err != nil { return err } - err = r.loadBundles(ctx, r.txn, r.metrics) - if err != nil { + if err := r.loadBundles(ctx, r.txn, r.metrics); err != nil { return err } - err = r.parseModules(ctx, r.txn, r.metrics) - if err != nil { + if err := r.parseModules(ctx, r.txn, r.metrics); err != nil { return err } // Compile the modules *before* the query, else functions // defined in the module won't be found... - err = r.compileModules(ctx, r.txn, r.metrics) - if err != nil { + if err := r.compileModules(ctx, r.txn, r.metrics); err != nil { return err } @@ -1977,7 +1979,7 @@ func (r *Rego) prepare(ctx context.Context, qType queryType, extras []extraStage return err } - queryImports := []*ast.Import{} + var queryImports []*ast.Import for _, imp := range imports { path := imp.Path.Value.(ast.Ref) if path.HasPrefix([]*ast.Term{ast.FutureRootDocument}) || path.HasPrefix([]*ast.Term{ast.RegoRootDocument}) { @@ -1985,17 +1987,11 @@ func (r *Rego) prepare(ctx context.Context, qType queryType, extras []extraStage } } - r.parsedQuery, err = r.parseQuery(queryImports, r.metrics) - if err != nil { - return err - } - - err = r.compileAndCacheQuery(qType, r.parsedQuery, imports, r.metrics, extras) - if err != nil { + if r.parsedQuery, err = r.parseQuery(queryImports, r.metrics); err != nil { return err } - return nil + return r.compileAndCacheQuery(qType, r.parsedQuery, imports, r.metrics, extras) } func (r *Rego) parseModules(ctx context.Context, txn storage.Transaction, m metrics.Metrics) error { @@ -2196,7 +2192,6 @@ func (r *Rego) compileModules(ctx context.Context, txn storage.Transaction, m me // Only compile again if there are new modules. if len(r.bundles) > 0 || len(r.parsedModules) > 0 { - // The bundle.Activate call will activate any bundles passed in // (ie compile + handle data store changes), and include any of // the additional modules passed in. If no bundles are provided @@ -2204,18 +2199,20 @@ func (r *Rego) compileModules(ctx context.Context, txn storage.Transaction, m me // Use this as the single-point of compiling everything only a // single time. opts := &bundle.ActivateOpts{ - Ctx: ctx, - Store: r.store, - Txn: txn, - Compiler: r.compilerForTxn(ctx, r.store, txn), - Metrics: m, - Bundles: r.bundles, - ExtraModules: r.parsedModules, - ParserOptions: ast.ParserOptions{RegoVersion: r.regoVersion}, - } - err := bundle.Activate(opts) - if err != nil { - return err + Ctx: ctx, + Store: r.store, + Txn: txn, + Compiler: r.compilerForTxn(ctx, r.store, txn), + Metrics: m, + Bundles: r.bundles, + ExtraModules: r.parsedModules, + ParserOptions: ast.ParserOptions{ + RegoVersion: r.regoVersion, + Capabilities: r.capabilities, + }, + } + if err := bundle.Activate(opts); err != nil { + return fmt.Errorf("bundle activation failed: %w", err) } } @@ -2261,11 +2258,13 @@ func (r *Rego) prepareImports() ([]*ast.Import, error) { imports := r.parsedImports if len(r.imports) > 0 { - s := make([]string, len(r.imports)) + var sb strings.Builder for i := range r.imports { - s[i] = fmt.Sprintf("import %v", r.imports[i]) + sb.WriteString("import ") + sb.WriteString(r.imports[i]) + sb.WriteByte('\n') } - parsed, err := ast.ParseImports(strings.Join(s, "\n")) + parsed, err := ast.ParseImports(sb.String()) if err != nil { return nil, err } @@ -2337,7 +2336,7 @@ func (r *Rego) eval(ctx context.Context, ectx *EvalContext) (ResultSet, error) { WithInterQueryBuiltinCache(ectx.interQueryBuiltinCache). WithInterQueryBuiltinValueCache(ectx.interQueryBuiltinValueCache). WithStrictBuiltinErrors(r.strictBuiltinErrors). - WithBuiltinErrorList(r.builtinErrorList). + WithBuiltinErrorList(ectx.builtinErrorList). WithSeed(ectx.seed). WithPrintHook(ectx.printHook). WithDistributedTracingOpts(r.distributedTracingOpts). @@ -2818,12 +2817,11 @@ func (*Rego) rewriteQueryForPartialEval(_ ast.QueryCompiler, query ast.Body) (as // where rewriting them can substantially simplify the result, and it is unlikely // that the caller would need expression values. func (*Rego) rewriteEqualsForPartialQueryCompile(_ ast.QueryCompiler, query ast.Body) (ast.Body, error) { - doubleEq := ast.Equal.Ref() unifyOp := ast.Equality.Ref() ast.WalkExprs(query, func(x *ast.Expr) bool { if x.IsCall() { operator := x.Operator() - if operator.Equal(doubleEq) && len(x.Operands()) == 2 { + if operator.Equal(ast.Interned.Refs.Equal) && len(x.Operands()) == 2 { x.SetOperator(ast.NewTerm(unifyOp)) } } @@ -3026,9 +3024,8 @@ func parseStringsToRefs(s []string) ([]ast.Ref, error) { // was defined. func finishFunction(name string, bctx topdown.BuiltinContext, result *ast.Term, err error, iter func(*ast.Term) error) error { if err != nil { - var e *HaltError sb := strings.Builder{} - if errors.As(err, &e) { + if e, ok := errors.AsType[*HaltError](err); ok { sb.Grow(len(name) + len(e.Error()) + 2) sb.WriteString(name) sb.WriteString(": ") diff --git a/vendor/github.com/open-policy-agent/opa/v1/storage/errors.go b/vendor/github.com/open-policy-agent/opa/v1/storage/errors.go index a3d1c00737..95bf25a156 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/storage/errors.go +++ b/vendor/github.com/open-policy-agent/opa/v1/storage/errors.go @@ -4,10 +4,6 @@ package storage -import ( - "fmt" -) - const ( // InternalErr indicates an unknown, internal error has occurred. InternalErr = "storage_internal_error" @@ -49,7 +45,7 @@ type Error struct { func (err *Error) Error() string { if err.Message != "" { - return fmt.Sprintf("%v: %v", err.Code, err.Message) + return err.Code + ": " + err.Message } return err.Code } diff --git a/vendor/github.com/open-policy-agent/opa/v1/storage/inmem/inmem.go b/vendor/github.com/open-policy-agent/opa/v1/storage/inmem/inmem.go index 8aa1fc9e42..77bfada098 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/storage/inmem/inmem.go +++ b/vendor/github.com/open-policy-agent/opa/v1/storage/inmem/inmem.go @@ -20,7 +20,6 @@ import ( "fmt" "io" "path/filepath" - "strings" "sync" "sync/atomic" @@ -116,7 +115,7 @@ func NewFromASTObject(data ast.Object) storage.Store { type store struct { rmu sync.RWMutex // reader-writer lock wmu sync.Mutex // writer lock - xid uint64 // last generated transaction id + xid atomic.Uint64 // last generated transaction id data any // raw or AST data policies map[string][]byte // raw policies triggers map[*handle]storage.TriggerConfig // registered triggers @@ -137,7 +136,7 @@ type handle struct { func (db *store) NewTransaction(_ context.Context, params ...storage.TransactionParams) (storage.Transaction, error) { txn := &transaction{ - xid: atomic.AddUint64(&db.xid, uint64(1)), + xid: db.xid.Add(1), db: db, } @@ -173,7 +172,7 @@ func (db *store) Truncate(ctx context.Context, txn storage.Transaction, params s } if update.IsPolicy { - err = underlying.UpsertPolicy(strings.TrimLeft(update.Path.String(), "/"), update.Value) + err = underlying.UpsertPolicy(update.Path.PolicyID(), update.Value) if err != nil { return err } @@ -183,11 +182,8 @@ func (db *store) Truncate(ctx context.Context, txn storage.Transaction, params s return err } - var key []string - dirpath := strings.TrimLeft(update.Path.String(), "/") - if len(dirpath) > 0 { - key = strings.Split(dirpath, "/") - } + // Do not round trip via String() to avoid URL encoding. + key := []string(update.Path) if value != nil { obj, err := mktree(key, value) diff --git a/vendor/github.com/open-policy-agent/opa/v1/storage/path.go b/vendor/github.com/open-policy-agent/opa/v1/storage/path.go index 16bb3e42c5..0143082adb 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/storage/path.go +++ b/vendor/github.com/open-policy-agent/opa/v1/storage/path.go @@ -9,10 +9,10 @@ import ( "fmt" "net/url" "slices" - "strconv" "strings" "github.com/open-policy-agent/opa/v1/ast" + "github.com/open-policy-agent/opa/v1/util" ) // RootPath refers to the root document in storage. @@ -102,11 +102,10 @@ func (p Path) Ref(head *ast.Term) (ref ast.Ref) { ref = make(ast.Ref, len(p)+1) ref[0] = head for i := range p { - idx, err := strconv.ParseInt(p[i], 10, 64) - if err == nil { - ref[i+1] = ast.UIntNumberTerm(uint64(idx)) + if idx, ok := util.Atoi(p[i]); ok && idx >= 0 { + ref[i+1] = ast.InternedTerm(idx) } else { - ref[i+1] = ast.StringTerm(p[i]) + ref[i+1] = ast.InternedTerm(p[i]) } } return ref @@ -131,6 +130,14 @@ func (p Path) String() string { return sb.String() } +// PolicyID returns the ID identifying the module stored at p, for use with the +// [Policy] interface: segments are joined verbatim, without the leading '/' and +// percent-encoding [Path.String] applies, so that IDs match the raw, unescaped +// bundle manifest roots they're compared against. +func (p Path) PolicyID() string { + return strings.Join(p, "/") +} + // MustParsePath returns a new Path for s. If s cannot be parsed, this function // will panic. This is mostly for test purposes. func MustParsePath(s string) Path { diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/aggregates.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/aggregates.go index f018057213..1005f522d1 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/aggregates.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/aggregates.go @@ -5,7 +5,9 @@ package topdown import ( + "math" "math/big" + "slices" "github.com/open-policy-agent/opa/v1/ast" "github.com/open-policy-agent/opa/v1/topdown/builtins" @@ -63,37 +65,50 @@ func exactIntAccumulate(a termIterable, init int64, op func(z, x, y *big.Int) *b return builtins.IntToNumber(acc), true } +// addInt returns x+y, reporting false if the sum overflows an int so the caller +// can fall back to exact big.Int accumulation instead of wrapping silently. +func addInt(x, y int) (int, bool) { + if (y > 0 && x > math.MaxInt-y) || (y < 0 && x < math.MinInt-y) { + return 0, false + } + return x + y, true +} + func builtinSum(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { switch a := operands[0].Value.(type) { case *ast.Array: // Fast path for arrays of integers is := 0 - nonInts := a.Until(func(x *ast.Term) bool { + bail := a.Until(func(x *ast.Term) bool { if n, ok := x.Value.(ast.Number); ok { if i, ok := n.Int(); ok { - is += i - return false + if s, ok := addInt(is, i); ok { + is = s + return false + } } } return true }) - if !nonInts { + if !bail { return iter(ast.InternedTerm(is)) } - // Non-integer values found, so we need to sum as floats. + // A non-integer element, or an integer sum that would overflow the + // machine int: accumulate on exact big.Ints, falling back to floats for + // genuinely non-integer input. if n, ok := exactIntAccumulate(a, 0, (*big.Int).Add); ok { return iter(ast.NewTerm(n)) } - sum := big.NewFloat(0) + sum := new(big.Float) tmp := new(big.Float) err := a.Iter(func(x *ast.Term) error { n, ok := x.Value.(ast.Number) if !ok { return builtins.NewOperandElementErr(1, a, x.Value, "number") } - sum = new(big.Float).Add(sum, builtins.NumberToFloatInto(tmp, n)) + sum = sum.Add(sum, builtins.NumberToFloatInto(tmp, n)) return nil }) if err != nil { @@ -103,16 +118,20 @@ func builtinSum(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) err case ast.Set: // Fast path for sets of integers is := 0 - nonInts := a.Until(func(x *ast.Term) bool { - if n, ok := x.Value.(ast.Number); ok { + bail := false + for _, term := range a.Slice() { + if n, ok := term.Value.(ast.Number); ok { if i, ok := n.Int(); ok { - is += i - return false + if s, ok := addInt(is, i); ok { + is = s + continue + } } } - return true - }) - if !nonInts { + bail = true + break + } + if !bail { return iter(ast.InternedTerm(is)) } @@ -120,18 +139,15 @@ func builtinSum(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) err return iter(ast.NewTerm(n)) } - sum := big.NewFloat(0) + sum := new(big.Float) tmp := new(big.Float) - err := a.Iter(func(x *ast.Term) error { - n, ok := x.Value.(ast.Number) + + for _, term := range a.Slice() { + n, ok := term.Value.(ast.Number) if !ok { - return builtins.NewOperandElementErr(1, a, x.Value, "number") + return builtins.NewOperandElementErr(1, a, term.Value, "number") } - sum = new(big.Float).Add(sum, builtins.NumberToFloatInto(tmp, n)) - return nil - }) - if err != nil { - return err + sum = sum.Add(sum, builtins.NumberToFloatInto(tmp, n)) } return iter(ast.NewTerm(builtins.FloatToNumber(sum))) } @@ -152,7 +168,7 @@ func builtinProduct(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) if !ok { return builtins.NewOperandElementErr(1, a, x.Value, "number") } - product = new(big.Float).Mul(product, builtins.NumberToFloatInto(tmp, n)) + product = product.Mul(product, builtins.NumberToFloatInto(tmp, n)) return nil }) if err != nil { @@ -171,7 +187,7 @@ func builtinProduct(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) if !ok { return builtins.NewOperandElementErr(1, a, x.Value, "number") } - product = new(big.Float).Mul(product, builtins.NumberToFloatInto(tmp, n)) + product = product.Mul(product, builtins.NumberToFloatInto(tmp, n)) return nil }) if err != nil { @@ -190,7 +206,7 @@ func builtinMax(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) err } max := ast.InternedNullTerm.Value a.Foreach(func(x *ast.Term) { - if ast.Compare(max, x.Value) <= 0 { + if max.Compare(x.Value) <= 0 { max = x.Value } }) @@ -199,16 +215,7 @@ func builtinMax(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) err if a.Len() == 0 { return nil } - max, err := a.Reduce(ast.InternedNullTerm, func(max *ast.Term, elem *ast.Term) (*ast.Term, error) { - if ast.Compare(max, elem) <= 0 { - return elem, nil - } - return max, nil - }) - if err != nil { - return err - } - return iter(max) + return iter(slices.MaxFunc(a.Slice(), ast.TermValueCompare)) } return builtins.NewOperandTypeErr(1, operands[0].Value, "set", "array") @@ -222,7 +229,7 @@ func builtinMin(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) err } min := a.Elem(0).Value a.Foreach(func(x *ast.Term) { - if ast.Compare(min, x.Value) >= 0 { + if min.Compare(x.Value) >= 0 { min = x.Value } }) @@ -231,23 +238,7 @@ func builtinMin(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) err if a.Len() == 0 { return nil } - min, err := a.Reduce(ast.InternedNullTerm, func(min *ast.Term, elem *ast.Term) (*ast.Term, error) { - // The null term is considered to be less than any other term, - // so in order for min of a set to make sense, we need to check - // for it. - if min.Value.Compare(ast.InternedNullValue) == 0 { - return elem, nil - } - - if ast.Compare(min, elem) >= 0 { - return elem, nil - } - return min, nil - }) - if err != nil { - return err - } - return iter(min) + return iter(slices.MinFunc(a.Slice(), ast.TermValueCompare)) } return builtins.NewOperandTypeErr(1, operands[0].Value, "set", "array") diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/arithmetic.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/arithmetic.go index ce870904f5..abdd6ea72d 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/arithmetic.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/arithmetic.go @@ -130,7 +130,10 @@ func arithDivide(a, b *big.Float) (*big.Float, error) { } func arithRem(a, b *big.Int) (*big.Int, error) { - if b.Int64() == 0 { + // Sign, not Int64: Int64 returns the low 64 bits when b does not fit in an + // int64, so any nonzero multiple of 2^64 (e.g. 10 % 18446744073709551616) + // would be misreported as modulo by zero. + if b.Sign() == 0 { return nil, errors.New("modulo by zero") } return new(big.Int).Rem(a, b), nil diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/builtins.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/builtins.go index b5a8a6714d..1f5cfb7f33 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/builtins.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/builtins.go @@ -7,7 +7,6 @@ package topdown import ( "context" "encoding/binary" - "fmt" "io" "math/rand" @@ -73,7 +72,6 @@ type ( // function. If a random number generator cannot be created, an error is // returned. func (bctx *BuiltinContext) Rand() (*rand.Rand, error) { - if bctx.rand != nil { return bctx.rand, nil } @@ -180,26 +178,18 @@ func functionalWrapper4(name string, fn FunctionalBuiltin4) BuiltinFunc { } func handleBuiltinErr(name string, loc *ast.Location, err error) error { + var code string switch err := err.(type) { case BuiltinEmpty: return nil case *Error, Halt: return err case builtins.ErrOperand: - e := &Error{ - Code: TypeErr, - Message: fmt.Sprintf("%v: %v", name, err.Error()), - Location: loc, - } - return e.Wrap(err) + code = TypeErr default: - e := &Error{ - Code: BuiltinErr, - Message: fmt.Sprintf("%v: %v", name, err.Error()), - Location: loc, - } - return e.Wrap(err) + code = BuiltinErr } + return (&Error{Code: code, Message: name + ": " + err.Error(), Location: loc}).Wrap(err) } func readInt64(r io.Reader) (int64, error) { diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/cache.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/cache.go index 9a162e2a57..e9bb12ab00 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/cache.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/cache.go @@ -229,8 +229,8 @@ func (s *refStack) Pop() { func (s *refStack) Prefixed(ref ast.Ref) bool { if s != nil { sl := s.sl.Slice() - for i := len(sl) - 1; i >= 0; i-- { - if slices.ContainsFunc(sl[i].refs, ref.HasPrefix) { + for _, s := range slices.Backward(sl) { + if slices.ContainsFunc(s.refs, ref.HasPrefix) { return true } } @@ -330,7 +330,7 @@ func (s *functionMocksStack) PopPairs() { } func (s *functionMocksStack) PutPairs(mocks [][2]*ast.Term) { - el := frame{} + el := make(frame, len(mocks)) for i := range mocks { el[mocks[i][0].Value.String()] = mocks[i][1] } @@ -347,8 +347,8 @@ func (s *functionMocksStack) Get(f ast.Ref) (*ast.Term, bool) { } current := s.stack.PeekGroup() - for i := len(current) - 1; i >= 0; i-- { - if r, ok := current[i][f.String()]; ok { + for _, c := range slices.Backward(current) { + if r, ok := c[f.String()]; ok { return r, true } } diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/cidr.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/cidr.go index e6e2bb3ae7..3f419a2931 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/cidr.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/cidr.go @@ -7,7 +7,6 @@ import ( "math/big" "net" "slices" - "sort" cidrMerge "github.com/open-policy-agent/opa/internal/cidr/merge" "github.com/open-policy-agent/opa/v1/ast" @@ -235,38 +234,15 @@ type cidrBlockRange struct { Network *net.IPNet } -type cidrBlockRanges []*cidrBlockRange - -// Implement Sort interface -func (c cidrBlockRanges) Len() int { - return len(c) -} - -func (c cidrBlockRanges) Swap(i, j int) { - c[i], c[j] = c[j], c[i] -} - -func (c cidrBlockRanges) Less(i, j int) bool { - // Compare last IP. - cmp := bytes.Compare(*c[i].Last, *c[j].Last) - if cmp < 0 { - return true - } else if cmp > 0 { - return false - } - - // Then compare first IP. - cmp = bytes.Compare(*c[i].First, *c[j].First) - if cmp < 0 { - return true - } else if cmp > 0 { - return false +func (c *cidrBlockRange) Compare(other *cidrBlockRange) int { + if cmp := bytes.Compare(*c.Last, *other.Last); cmp != 0 { // Compare last IP. + return cmp } - - // Ranges are Equal. - return false + return bytes.Compare(*c.First, *other.First) // Then compare first IP. } +type cidrBlockRanges []*cidrBlockRange + // builtinNetCIDRMerge merges the provided list of IP addresses and subnets into the smallest possible list of CIDRs. // It merges adjacent subnets where possible, those contained within others and also removes any duplicates. // Original Algorithm: https://github.com/netaddr/netaddr. @@ -283,16 +259,12 @@ func builtinNetCIDRMerge(_ BuiltinContext, operands []*ast.Term, iter func(*ast. networks = append(networks, network) } case ast.Set: - err := v.Iter(func(x *ast.Term) error { - network, err := generateIPNet(x) + for _, term := range v.Slice() { + network, err := generateIPNet(term) if err != nil { return err } networks = append(networks, network) - return nil - }) - if err != nil { - return err } default: return errors.New("operand must be an array") @@ -367,7 +339,7 @@ func generateIPNet(term *ast.Term) (*net.IPNet, error) { } func mergeCIDRs(ranges cidrBlockRanges) cidrBlockRanges { - sort.Sort(ranges) + slices.SortFunc(ranges, (*cidrBlockRange).Compare) // Merge adjacent CIDRs if possible. for i := len(ranges) - 1; i > 0; i-- { diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/comparison.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/comparison.go index d0434a028b..e5f34a2919 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/comparison.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/comparison.go @@ -6,43 +6,35 @@ package topdown import "github.com/open-policy-agent/opa/v1/ast" -type compareFunc func(a, b ast.Value) bool - -func compareGreaterThan(a, b ast.Value) bool { - return a.Compare(b) > 0 -} - -func compareGreaterThanEq(a, b ast.Value) bool { - return a.Compare(b) >= 0 +func builtinGreaterThan(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + return iter(ast.InternedTerm(operands[0].Value.Compare(operands[1].Value) > 0)) } -func compareLessThan(a, b ast.Value) bool { - return a.Compare(b) < 0 +func builtinGreaterThanEq(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + return iter(ast.InternedTerm(operands[0].Value.Compare(operands[1].Value) >= 0)) } -func compareLessThanEq(a, b ast.Value) bool { - return a.Compare(b) <= 0 +func builtinLessThan(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + return iter(ast.InternedTerm(operands[0].Value.Compare(operands[1].Value) < 0)) } -func compareNotEq(a, b ast.Value) bool { - return a.Compare(b) != 0 +func builtinLessThanEq(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + return iter(ast.InternedTerm(operands[0].Value.Compare(operands[1].Value) <= 0)) } -func compareEq(a, b ast.Value) bool { - return a.Compare(b) == 0 +func builtinNotEqual(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + return iter(ast.InternedTerm(!operands[0].Equal(operands[1]))) } -func builtinCompare(cmp compareFunc) BuiltinFunc { - return func(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - return iter(ast.InternedTerm(cmp(operands[0].Value, operands[1].Value))) - } +func builtinEqual(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { + return iter(ast.InternedTerm(operands[0].Equal(operands[1]))) } func init() { - RegisterBuiltinFunc(ast.GreaterThan.Name, builtinCompare(compareGreaterThan)) - RegisterBuiltinFunc(ast.GreaterThanEq.Name, builtinCompare(compareGreaterThanEq)) - RegisterBuiltinFunc(ast.LessThan.Name, builtinCompare(compareLessThan)) - RegisterBuiltinFunc(ast.LessThanEq.Name, builtinCompare(compareLessThanEq)) - RegisterBuiltinFunc(ast.NotEqual.Name, builtinCompare(compareNotEq)) - RegisterBuiltinFunc(ast.Equal.Name, builtinCompare(compareEq)) + RegisterBuiltinFunc(ast.GreaterThan.Name, builtinGreaterThan) + RegisterBuiltinFunc(ast.GreaterThanEq.Name, builtinGreaterThanEq) + RegisterBuiltinFunc(ast.LessThan.Name, builtinLessThan) + RegisterBuiltinFunc(ast.LessThanEq.Name, builtinLessThanEq) + RegisterBuiltinFunc(ast.NotEqual.Name, builtinNotEqual) + RegisterBuiltinFunc(ast.Equal.Name, builtinEqual) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/copypropagation/copypropagation.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/copypropagation/copypropagation.go index 799a716136..e086e5d418 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/copypropagation/copypropagation.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/copypropagation/copypropagation.go @@ -6,7 +6,6 @@ package copypropagation import ( "fmt" - "sort" "github.com/open-policy-agent/opa/v1/ast" "github.com/open-policy-agent/opa/v1/util" @@ -374,11 +373,10 @@ func (p *CopyPropagator) placeholderRef(b *binding) *ast.Expr { if !ok || !p.placeholders.Contains(k) { return nil } - ref, ok := b.v.(ast.Ref) - if !ok { + if _, ok = b.v.(ast.Ref); !ok { return nil } - return ast.NewExpr(ast.NewTerm(ref)) + return ast.NewExpr(ast.NewTerm(b.v)) } func (p *CopyPropagator) updateBindingsEq(a, b *ast.Term) (ast.Var, ast.Value, bool) { @@ -475,10 +473,9 @@ func sortbindings(bindings *ast.ValueMap) []*binding { sorted = append(sorted, &binding{k, v}) return false }) - sort.Slice(sorted, func(i, j int) bool { - return sorted[i].k.Compare(sorted[j].k) > 0 + return util.SortedFunc(sorted, func(a, b *binding) int { + return b.k.Compare(a.k) }) - return sorted } // makeDisjointSets builds the union-find structure for the query. The structure @@ -497,24 +494,22 @@ func makeDisjointSets(livevars ast.VarSet, query ast.Body) (*unionFind, bool) { for _, expr := range query { if expr.IsEquality() && !expr.Negated && len(expr.With) == 0 { a, b := expr.Operand(0), expr.Operand(1) - varA, ok1 := a.Value.(ast.Var) - varB, ok2 := b.Value.(ast.Var) + _, aIsVar := a.Value.(ast.Var) + _, bIsVar := b.Value.(ast.Var) switch { - case ok1 && ok2: - if _, ok := uf.Merge(varA, varB); !ok { + case aIsVar && bIsVar: + if _, ok := uf.Merge(a.Value, b.Value); !ok { return nil, false } - - case ok1 && ast.IsConstant(b.Value): - root := uf.MakeSet(varA) + case aIsVar && ast.IsConstant(b.Value): + root := uf.MakeSet(a.Value) if root.constant != nil && !root.constant.Equal(b) { return nil, false } root.constant = b - - case ok2 && ast.IsConstant(a.Value): - root := uf.MakeSet(varB) + case bIsVar && ast.IsConstant(a.Value): + root := uf.MakeSet(b.Value) if root.constant != nil && !root.constant.Equal(a) { return nil, false } @@ -527,11 +522,10 @@ func makeDisjointSets(livevars ast.VarSet, query ast.Body) (*unionFind, bool) { } func isNoop(expr *ast.Expr) bool { - switch t := expr.Terms.(type) { case []*ast.Term: // A==A can be ignored - if expr.Operator().Equal(ast.Equal.Ref()) { + if expr.Operator().Equal(ast.Interned.Refs.Equal) { return expr.Operand(0).Equal(expr.Operand(1)) } return false diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/errors.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/errors.go index e80339e312..e2dc597ace 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/errors.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/errors.go @@ -6,6 +6,7 @@ package topdown import ( "errors" + "strconv" "github.com/open-policy-agent/opa/v1/ast" "github.com/open-policy-agent/opa/v1/util" @@ -72,8 +73,7 @@ func IsCancel(err error) bool { // Is allows matching topdown errors using errors.Is (see IsCancel). func (e *Error) Is(target error) bool { - var t *Error - if errors.As(target, &t) { + if t, ok := errors.AsType[*Error](target); ok { return (t.Code == "" || e.Code == t.Code) && (t.Message == "" || e.Message == t.Message) && (t.Location == nil || t.Location.Compare(e.Location) == 0) @@ -154,6 +154,18 @@ func mergeConflictErr(loc *ast.Location) error { } } +// unevaluatedOperandErr is returned when a built-in function would have been +// called with an operand that requires evaluation, which indicates a bug in OPA +// rather than in the policy being evaluated. +func unevaluatedOperandErr(loc *ast.Location, name string, pos int, operand *ast.Term) error { + return &Error{ + Code: InternalErr, + Location: loc, + Message: "built-in function " + name + " called with operand " + strconv.Itoa(pos) + + " that requires evaluation: " + operand.String(), + } +} + func internalErr(loc *ast.Location, msg string) error { return &Error{ Code: InternalErr, diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/eval.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/eval.go index f22af29267..457bd38ba8 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/eval.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/eval.go @@ -192,6 +192,7 @@ func (e *eval) closure(query ast.Body, cpy *eval) { cpy.queryID = cpy.queryIDFact.Next() cpy.parent = e cpy.findOne = false + cpy.defined = false } // childWithBindingSizeHint creates a child evaluator with bindings pre-sized for the expected number of variables. @@ -203,6 +204,7 @@ func (e *eval) childWithBindingSizeHint(query ast.Body, cpy *eval, sizeHint int) cpy.bindings = newBindingsWithSize(cpy.queryID, e.instr, sizeHint) cpy.parent = e cpy.findOne = false + cpy.defined = false } func (e *eval) next(iter evalIterator) error { @@ -418,15 +420,12 @@ func (e *eval) evalExpr(iter evalIterator) error { }) } -func (e *eval) evalStep(iter evalIterator) error { +func (e *eval) evalStep(iter evalIterator) (err error) { expr := e.query[e.index] - if expr.Negated { return e.evalNot(iter) } - var err error - // NOTE(æ): the reason why there's one branch for the tracing case and one almost // identical branch below for when tracing is disabled is that the tracing case // allocates wildly. These allocations are cause by the "defined" boolean variable @@ -629,7 +628,6 @@ func (e *eval) fmtVar() string { func (e *eval) evalNot(iter evalIterator) error { expr := e.query[e.index] - if e.unknown(expr, e.bindings) { return e.setupAndEvalNotPartial(iter) } @@ -639,7 +637,6 @@ func (e *eval) evalNot(iter evalIterator) error { defer evalPool.Put(child) e.closure(negation, child) - if e.traceEnabled { child.traceEnter(negation) } @@ -660,8 +657,6 @@ func (e *eval) evalNot(iter evalIterator) error { return iter(e) } - child.defined = false - e.traceFail(expr) return nil } @@ -793,24 +788,15 @@ func (e *eval) evalWithPush(input, data *ast.Term, functionMocks [][2]*ast.Term, e.data = data } - if e.comprehensionCache == nil { - e.comprehensionCache = newComprehensionCache() - } - + e.comprehensionCache = util.Or(e.comprehensionCache, newComprehensionCache) e.comprehensionCache.Push() e.virtualCache.Push() - if e.targetStack == nil { - e.targetStack = newRefStack() - } - + e.targetStack = util.Or(e.targetStack, newRefStack) e.targetStack.Push(targets) e.inliningControl.PushDisable(disable, true) - if e.functionMocks == nil { - e.functionMocks = newFunctionMocksStack() - } - + e.functionMocks = util.Or(e.functionMocks, newFunctionMocksStack) e.functionMocks.PutPairs(functionMocks) return oldInput, oldData, pushedFrame @@ -998,7 +984,8 @@ func (e *eval) evalCall(terms []*ast.Term, iter unifyIterator) error { mock, mocked := e.functionMocks.Get(ref) if mocked { if m, ok := mock.Value.(ast.Ref); ok && isFunction(e.compiler.TypeEnv, m) { // builtin or data function - mockCall := append([]*ast.Term{mock}, terms[1:]...) + mockCall := make([]*ast.Term, 0, len(terms)) + mockCall = append(append(mockCall, mock), terms[1:]...) e.functionMocks.Push() err := e.evalCall(mockCall, func() error { @@ -1410,16 +1397,13 @@ func (e *eval) biunifyComprehension(a, b *ast.Term, b1, b2 *bindings, swap bool, } func (e *eval) buildComprehensionCache(a *ast.Term) (*ast.Term, error) { - index := e.comprehensionIndex(a) if index == nil { e.instr.counterIncr(evalOpComprehensionCacheSkip) return nil, nil } - if e.comprehensionCache == nil { - e.comprehensionCache = newComprehensionCache() - } + e.comprehensionCache = util.Or(e.comprehensionCache, newComprehensionCache) cache, ok := e.comprehensionCache.Elem(a) if !ok { @@ -2076,6 +2060,21 @@ func (e *evalBuiltin) canUseNDBCache(bi *ast.Builtin) bool { return bi.Nondeterministic && e.bctx != nil && e.bctx.NDBuiltinCache != nil } +// operandRequiresEval returns true if a plugged built-in operand still contains +// terms that must be evaluated. ast.IsConstant answers this exactly, but walks +// composites, making the check linear in operand size on every built-in call. +// This stays O(1) -- IsGround is a cached field on composites -- at the cost of +// missing nested terms that require evaluation but are ground (e.g. [data.foo]). +func operandRequiresEval(v ast.Value) bool { + switch v.(type) { + case ast.Var, ast.Ref, ast.Call, + *ast.ArrayComprehension, *ast.ObjectComprehension, *ast.SetComprehension: + return true + } + + return !v.IsGround() +} + func (e *evalBuiltin) eval(iter unifyIterator) error { operands := make([]*ast.Term, len(e.terms)) @@ -2086,8 +2085,6 @@ func (e *evalBuiltin) eval(iter unifyIterator) error { numDeclArgs := e.bi.Decl.Arity() - e.e.instr.startTimer(evalOpBuiltinCall) - // NOTE(philipc): We sometimes have to drop the very last term off // the args list for cases where a builtin's result is used/assigned, // because the last term will be a generated term, not an actual @@ -2097,6 +2094,24 @@ func (e *evalBuiltin) eval(iter unifyIterator) error { endIndex-- } + // Every operand must be ground, except a captured output -- walk() is called + // with a non-ground composite there. Void built-ins have none, and Arity() + // undercounts the variadic ones (always void), so endIndex can't be used. + checkEnd := endIndex + if e.bi.Decl.Result() == nil { + checkEnd = len(operands) + } + + for i, operand := range operands[:checkEnd] { + if operandRequiresEval(operand.Value) { + // If hit, this is a bug: the compiler hoists arguments that require evaluation. + // Fail loudly, as the built-in would return undefined instead leading to unexpected results. + return unevaluatedOperandErr(e.e.query[e.e.index].Location, e.bi.Name, i+1, operand) + } + } + + e.e.instr.startTimer(evalOpBuiltinCall) + // We skip evaluation of the builtin entirely if the NDBCache is // present, and we have a non-deterministic builtin already cached. if e.canUseNDBCache(e.bi) { @@ -2755,6 +2770,13 @@ func (e evalTree) enumerate(iter unifyIterator) error { doc, err := e.e.Resolve(e.plugged[:e.pos]) if err != nil { + // The save set check in biunifyValues compares refs as written, so a ref + // that only becomes unknown once bindings are plugged (e.g. + // data[input.type].x with data.project.x unknown) reaches here, where the + // document can't be enumerated and must be saved as evalTree.finish does. + if ast.IsUnknownValueErr(err) { + return e.e.saveUnify(ast.NewTerm(e.plugged), e.rterm, e.bindings, e.rbindings, iter) + } return err } @@ -2810,6 +2832,13 @@ func (e evalTree) enumerate(iter unifyIterator) error { // Reuse the same enumerateNext for virtual documents for _, k := range e.node.Sorted { key := ast.NewTerm(k) + + // next() descends into both the base document and the rule tree, so + // enumerating a key present in both would yield it twice. + if docHasKey(doc, key) { + continue + } + en.key = key if err := e.e.biunify(key, e.ref[e.pos], e.bindings, e.bindings, en.call); err != nil { return err @@ -2819,6 +2848,25 @@ func (e evalTree) enumerate(iter unifyIterator) error { return nil } +// docHasKey returns true if key is one of the keys evalTree.enumerate yields +// for the base document doc. +func docHasKey(doc ast.Value, key *ast.Term) bool { + switch doc := doc.(type) { + case ast.Object: + return doc.Get(key) != nil + case *ast.Array: + i, ok := key.Value.(ast.Number) + if !ok { + return false + } + idx, ok := i.Int() + return ok && idx >= 0 && idx < doc.Len() + case ast.Set: + return doc.Contains(key) + } + return false +} + func (e evalTree) extent() (*ast.Term, error) { base, err := e.e.Resolve(e.plugged) if err != nil { @@ -3003,7 +3051,6 @@ func (h *evalVirtualPartialCacheHint) keyWithoutScope() ast.Ref { } func (e evalVirtualPartial) eval(iter unifyIterator) error { - unknown := e.e.unknown(e.ref[:e.pos+1], e.bindings) if len(e.ref) == e.pos+1 { @@ -3038,7 +3085,6 @@ func maxRefLength(rules []*ast.Rule, ceil int) int { } func (e evalVirtualPartial) evalEachRule(iter unifyIterator, unknown bool) error { - if e.ir.Empty() { return nil } @@ -3095,7 +3141,6 @@ func (e evalVirtualPartial) evalEachRule(iter unifyIterator, unknown bool) error } func (e evalVirtualPartial) evalAllRules(iter unifyIterator, rules []*ast.Rule) error { - cacheKey := e.plugged[:e.pos+1] result, _ := e.e.virtualCache.Get(cacheKey) if result != nil { @@ -3128,17 +3173,16 @@ func (e evalVirtualPartial) evalAllRulesNoCache(rules []*ast.Rule) (*ast.Term, e for _, rule := range rules { e.e.childWithBindingSizeHint(rule.Body, child, ast.EstimateBodyBindingCount(rule.Body)) child.traceEnter(rule) - err := child.eval(func(*eval) error { + err := child.eval(func(*eval) (err error) { child.traceExit(rule) e.e.evaluated.Record(rule) - var err error + result, _, err = e.reduce(rule, child.bindings, result, &visitedRefs) - if err != nil { - return err + if err == nil && child.traceEnabled { + child.traceRedo(rule) } - child.traceRedo(rule) - return nil + return err }) if err != nil { @@ -3159,14 +3203,20 @@ func wrapInObjects(leaf *ast.Term, ref ast.Ref) *ast.Term { return ast.ObjectTerm(ast.Item(key, val)) } -func (e evalVirtualPartial) evalOneRulePreUnify(iter unifyIterator, rule *ast.Rule, result *ast.Term, unknown bool, visitedRefs *[]ast.Ref) (*ast.Term, error) { +func (e evalVirtualPartial) evalOneRulePreUnify( + iter unifyIterator, + rule *ast.Rule, + result *ast.Term, + unknown bool, + visitedRefs *[]ast.Ref, +) (*ast.Term, error) { child := evalPool.Get() defer evalPool.Put(child) e.e.childWithBindingSizeHint(rule.Body, child, ast.EstimateBodyBindingCount(rule.Body)) - - child.traceEnter(rule) - var defined bool + if child.traceEnabled { + child.traceEnter(rule) + } headKey := rule.Head.Key if headKey == nil { @@ -3174,11 +3224,17 @@ func (e evalVirtualPartial) evalOneRulePreUnify(iter unifyIterator, rule *ast.Ru } // Walk the dynamic portion of rule ref and key to unify vars - err := child.biunifyRuleHead(e.pos+1, e.ref, rule, e.bindings, child.bindings, func(_ int) error { - defined = true - return child.eval(func(child *eval) error { - - child.traceExit(rule) + err := child.biunifyRuleHead(e.pos+1, e.ref, rule, e.bindings, child.bindings, func(int) error { + child.defined = true + return child.eval(func(child *eval) (err error) { + if child.traceEnabled { + child.traceExit(rule) + defer func() { + if err == nil { + child.traceRedo(rule) + } + }() + } term := rule.Head.Value if term == nil { @@ -3187,7 +3243,6 @@ func (e evalVirtualPartial) evalOneRulePreUnify(iter unifyIterator, rule *ast.Ru if unknown { term, termbindings := child.bindings.apply(term) - if rule.Head.RuleKind() == ast.MultiValue { term = ast.SetTerm(term) } @@ -3195,37 +3250,24 @@ func (e evalVirtualPartial) evalOneRulePreUnify(iter unifyIterator, rule *ast.Ru objRef := rule.Ref()[e.pos+1:] term = wrapInObjects(term, objRef) - err := e.evalTerm(iter, e.pos+1, term, termbindings) - if err != nil { - return err - } + err = e.evalTerm(iter, e.pos+1, term, termbindings) } else { var dup bool - var err error result, dup, err = e.reduce(rule, child.bindings, result, visitedRefs) - if err != nil { - return err - } else if !unknown && dup { + if err == nil && !unknown && dup && child.traceEnabled { child.traceDuplicate(rule) - return nil } } - child.traceRedo(rule) - - return nil + return err }) }) - if err != nil { - return nil, err - } - - if !defined { + if err == nil && child.traceEnabled && !child.defined { child.traceFail(rule) } - return result, nil + return result, err } func (e *eval) biunifyRuleHead(pos int, ref ast.Ref, rule *ast.Rule, refBindings, ruleBindings *bindings, iter unifyRefIterator) error { @@ -3256,34 +3298,30 @@ func (e *eval) biunifyDynamicRef(pos int, a, b ast.Ref, b1, b2 *bindings, iter u func (e evalVirtualPartial) evalOneRulePostUnify(iter unifyIterator, rule *ast.Rule) error { child := evalPool.Get() - defer evalPool.Put(child) + defer func() { + if child.traceEnabled && !child.defined { + child.traceFail(rule) + } + evalPool.Put(child) + }() e.e.childWithBindingSizeHint(rule.Body, child, ast.EstimateBodyBindingCount(rule.Body)) + if e.e.traceEnabled { + child.traceEnter(rule) + } - child.traceEnter(rule) - var defined bool - - err := child.eval(func(child *eval) error { - defined = true - return e.e.biunifyRuleHead(e.pos+1, e.ref, rule, e.bindings, child.bindings, func(_ int) error { - return e.evalOneRuleContinue(iter, rule, child) + return child.eval(func(next *eval) error { + child.defined = true + return e.e.biunifyRuleHead(e.pos+1, e.ref, rule, e.bindings, next.bindings, func(int) error { + return e.evalOneRuleContinue(iter, rule, next) }) }) - - if err != nil { - return err - } - - if !defined { - child.traceFail(rule) - } - - return nil } func (e evalVirtualPartial) evalOneRuleContinue(iter unifyIterator, rule *ast.Rule, child *eval) error { - - child.traceExit(rule) + if child.traceEnabled { + child.traceExit(rule) + } term := rule.Head.Value if term == nil { @@ -3291,7 +3329,6 @@ func (e evalVirtualPartial) evalOneRuleContinue(iter unifyIterator, rule *ast.Ru } term, termbindings := child.bindings.apply(term) - if rule.Head.RuleKind() == ast.MultiValue { term = ast.SetTerm(term) } @@ -3300,16 +3337,14 @@ func (e evalVirtualPartial) evalOneRuleContinue(iter unifyIterator, rule *ast.Ru term = wrapInObjects(term, objRef) err := e.evalTerm(iter, e.pos+1, term, termbindings) - if err != nil { - return err + if child.traceEnabled && err == nil { + child.traceRedo(rule) } - child.traceRedo(rule) - return nil + return err } func (e evalVirtualPartial) partialEvalSupport(iter unifyIterator) error { - path := e.e.namespaceRef(e.plugged[:e.pos+1]) term := ast.NewTerm(e.e.namespaceRef(e.ref)) @@ -3604,9 +3639,10 @@ func (q vcKeyScope) AppendText(buf []byte) ([]byte, error) { // reduce removes vars from the tail of the ref. func (q vcKeyScope) reduce() vcKeyScope { ref := q.Ref.CopyNonGround() - var i int - for i = len(q.Ref) - 1; i >= 0; i-- { - if _, ok := q.Ref[i].Value.(ast.Var); !ok { + i := -1 + for idx, v := range slices.Backward(q.Ref) { + if _, ok := v.Value.(ast.Var); !ok { + i = idx break } } @@ -4060,8 +4096,7 @@ func (e evalTerm) next(iter unifyIterator, plugged *ast.Term) error { func (e evalTerm) enumerate(iter unifyIterator) error { var deferredEe *deferredEarlyExitError handleErr := func(err error) error { - var dee *deferredEarlyExitError - if errors.As(err, &dee) { + if dee, ok := errors.AsType[*deferredEarlyExitError](err); ok { if deferredEe == nil { deferredEe = dee } @@ -4327,7 +4362,6 @@ func (e evalNot) eval(iter evalIterator) error { defer evalPool.Put(child) e.e.closure(e.not.Body, child) - if e.e.traceEnabled { child.traceEnter(e.not.Body) } @@ -4545,13 +4579,12 @@ func evalLogicalOperand(parent *eval, body ast.Body) (bool, error) { child.traceEnter(body) } - defined := false err := child.eval(func(*eval) error { if parent.traceEnabled { child.traceExit(body) child.traceRedo(body) } - defined = true + child.defined = true return nil }) @@ -4561,7 +4594,7 @@ func evalLogicalOperand(parent *eval, body ast.Body) (bool, error) { return false, err } - return defined, nil + return child.defined, nil } func plugBody(e *eval, body ast.Body) error { @@ -4655,8 +4688,7 @@ func getSavePairsFromExpr(declArgsLen int, x *ast.Expr, b *bindings, result []sa func getSavePairsFromTerm(x *ast.Term, b *bindings, result []savePair) []savePair { if _, ok := x.Value.(ast.Var); ok { - result = append(result, savePair{x, b}) - return result + return append(result, savePair{x, b}) } vis := ast.NewVarVisitor().WithParams(ast.VarVisitorParams{ SkipClosures: true, @@ -4699,6 +4731,14 @@ func canInlineNegation(safe ast.VarSet, queries []ast.Body) bool { for _, query := range queries { size *= len(query) + + // NOTE(tsandall): this limit is arbitrary–it's only in place to prevent the + // partial evaluation result from blowing up. In the future, we could make this + // configurable or do something more clever. + if size > maxInlineNegationSize { + return false + } + for _, expr := range query { if containsNestedRefOrCall(vis, expr) { // Expressions containing nested refs or calls cannot be trivially negated @@ -4726,12 +4766,13 @@ func canInlineNegation(safe ast.VarSet, queries []ast.Body) bool { } } - // NOTE(tsandall): this limit is arbitrary–it's only in place to prevent the - // partial evaluation result from blowing up. In the future, we could make this - // configurable or do something more clever. - return size <= 16 + return true } +// maxInlineNegationSize is the largest cross product of negated queries that +// evalNotPartial will inline instead of generating support rules for. +const maxInlineNegationSize = 16 + type nestedCheckVisitor struct { vis *ast.GenericVisitor found bool @@ -4964,9 +5005,9 @@ func (e *eval) updateSavedMocks(withs []*ast.With) []*ast.With { // tree levels. keys are the ground parameter terms in reference order. func wrapExternalParams(keys []*ast.Term, tree *ast.TreeNode) *ast.TreeNode { node := tree - for i := len(keys) - 1; i >= 0; i-- { + for _, key := range slices.Backward(keys) { node = &ast.TreeNode{ - Children: map[ast.Value]*ast.TreeNode{keys[i].Value: node}, + Children: map[ast.Value]*ast.TreeNode{key.Value: node}, } } return node diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/http.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/http.go index 79d49f33ca..62fd92c351 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/http.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/http.go @@ -33,80 +33,14 @@ import ( "github.com/open-policy-agent/opa/v1/util" ) -type cachingMode string - const ( defaultHTTPRequestTimeoutEnv = "HTTP_SEND_TIMEOUT" defaultCachingMode cachingMode = "serialized" cachingModeDeserialized cachingMode = "deserialized" -) - -var defaultHTTPRequestTimeout = time.Second * 5 - -var allowedKeyNames = [...]string{ - "method", - "url", - "body", - "enable_redirect", - "force_json_decode", - "force_yaml_decode", - "headers", - "raw_body", - "tls_use_system_certs", - "tls_ca_cert", - "tls_ca_cert_file", - "tls_ca_cert_env_variable", - "tls_client_cert", - "tls_client_cert_file", - "tls_client_cert_env_variable", - "tls_client_key", - "tls_client_key_file", - "tls_client_key_env_variable", - "tls_insecure_skip_verify", - "tls_server_name", - "timeout", - "cache", - "force_cache", - "force_cache_duration_seconds", - "raise_error", - "caching_mode", - "max_retry_attempts", - "cache_ignored_headers", -} - -// ref: https://www.rfc-editor.org/rfc/rfc7231#section-6.1 -var cacheableHTTPStatusCodes = [...]int{ - http.StatusOK, - http.StatusNonAuthoritativeInfo, - http.StatusNoContent, - http.StatusPartialContent, - http.StatusMultipleChoices, - http.StatusMovedPermanently, - http.StatusNotFound, - http.StatusMethodNotAllowed, - http.StatusGone, - http.StatusRequestURITooLong, - http.StatusNotImplemented, -} + httpSendLatencyMetricKey = "rego_builtin_http_send" + httpSendInterQueryCacheHits = httpSendLatencyMetricKey + "_interquery_cache_hits" + httpSendNetworkRequests = httpSendLatencyMetricKey + "_network_requests" -var ( - httpSendNetworkErrTerm, httpSendInternalErrTerm *ast.Term - - allowedKeys = ast.NewSet() - cacheableCodes = ast.NewSet() - requiredKeys = ast.NewSet(ast.InternedTerm("method"), ast.InternedTerm("url")) - httpSendLatencyMetricKey = "rego_builtin_http_send" - httpSendInterQueryCacheHits = httpSendLatencyMetricKey + "_interquery_cache_hits" - httpSendNetworkRequests = httpSendLatencyMetricKey + "_network_requests" -) - -type httpSendKey string - -// CustomizeRoundTripper allows customizing an existing http.Transport, -// to the returned value, which could be the same Transport or a new one. -type CustomizeRoundTripper func(*http.Transport) http.RoundTripper - -const ( // httpSendBuiltinCacheKey is the key in the builtin context cache that // points to the http.send() specific cache resides at. httpSendBuiltinCacheKey httpSendKey = "HTTP_SEND_CACHE_KEY" @@ -124,8 +58,117 @@ const ( maxRetryDelay = time.Second * 60 ) -func builtinHTTPSend(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { +var ( + defaultHTTPRequestTimeout = time.Second * 5 + allowedKeyNames = [...]string{ + "method", + "url", + "body", + "enable_redirect", + "force_json_decode", + "force_yaml_decode", + "headers", + "raw_body", + "tls_use_system_certs", + "tls_ca_cert", + "tls_ca_cert_file", + "tls_ca_cert_env_variable", + "tls_client_cert", + "tls_client_cert_file", + "tls_client_cert_env_variable", + "tls_client_key", + "tls_client_key_file", + "tls_client_key_env_variable", + "tls_insecure_skip_verify", + "tls_server_name", + "timeout", + "cache", + "force_cache", + "force_cache_duration_seconds", + "raise_error", + "caching_mode", + "max_retry_attempts", + "cache_ignored_headers", + } + // ref: https://www.rfc-editor.org/rfc/rfc7231#section-6.1 + cacheableHTTPStatusCodes = [...]int{ + http.StatusOK, + http.StatusNonAuthoritativeInfo, + http.StatusNoContent, + http.StatusPartialContent, + http.StatusMultipleChoices, + http.StatusMovedPermanently, + http.StatusNotFound, + http.StatusMethodNotAllowed, + http.StatusGone, + http.StatusRequestURITooLong, + http.StatusNotImplemented, + } + + allowedKeys = ast.NewSet() + cacheableCodes = ast.NewSet() + requiredKeys = ast.NewSet(ast.InternedTerm("method"), ast.InternedTerm("url")) +) +type ( + // CustomizeRoundTripper allows customizing an existing http.Transport, + // to the returned value, which could be the same Transport or a new one. + CustomizeRoundTripper func(*http.Transport) http.RoundTripper + cachingMode string + httpSendKey string + interQueryCacheValue struct { + Data []byte + } + // httpRequestExecutor defines an interface for the http send cache + httpRequestExecutor interface { + CheckCache() (ast.Value, error) + InsertIntoCache(value *http.Response) (ast.Value, error) + InsertErrorIntoCache(err error) + ExecuteHTTPRequest() (*http.Response, error) + } + // The httpSendCache is used for intra-query caching of http.send results. + httpSendCache struct { + entries *util.HasherMap[ast.Value, httpSendCacheEntry] + } + httpSendCacheEntry struct { + response *ast.Value + error error + } + interQueryCache struct { + bctx BuiltinContext + req ast.Object + key ast.Object + httpReq *http.Request + httpClient *http.Client + forceJSONDecode bool + forceYAMLDecode bool + forceCacheParams forceCacheParams + } + interQueryCacheData struct { + RespBody []byte + Status string + StatusCode int + Headers http.Header + ExpiresAt time.Time + } + intraQueryCache struct { + bctx BuiltinContext + req ast.Object + key ast.Object + } + forceCacheParams struct { + forceDurationSeconds int32 + } + responseHeaders struct { + etag string // identifier for a specific version of the response + lastModified string // date and time response was last modified as per origin server + } + // deltaSeconds specifies a non-negative integer, representing + // time in seconds: http://tools.ietf.org/html/rfc7234#section-1.2.1 + deltaSeconds int32 +) + +func builtinHTTPSend(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { obj, err := builtins.ObjectOperand(operands[0].Value, 1) if err != nil { return handleBuiltinErr(ast.HTTPSend.Name, bctx.Location, err) @@ -139,7 +182,7 @@ func builtinHTTPSend(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.T req, err := validateHTTPRequestOperand(operands[0], 1) if err != nil { if raiseError { - return handleHTTPSendErr(bctx, err) + return handleHTTPSendErr(bctx.Context, bctx.Location, err) } return iter(generateRaiseErrorResult(handleBuiltinErr(ast.HTTPSend.Name, bctx.Location, err))) @@ -148,9 +191,8 @@ func builtinHTTPSend(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.T result, err := getHTTPResponse(bctx, req) if err != nil { if raiseError { - return handleHTTPSendErr(bctx, err) + return handleHTTPSendErr(bctx.Context, bctx.Location, err) } - result = generateRaiseErrorResult(err) } return iter(result) @@ -161,12 +203,12 @@ func generateRaiseErrorResult(err error) *ast.Term { switch err.(type) { case *url.Error: errObj = ast.NewObject( - ast.Item(ast.InternedTerm("code"), httpSendNetworkErrTerm), + ast.Item(ast.InternedTerm("code"), ast.InternedTerm(HTTPSendNetworkErr)), ast.Item(ast.InternedTerm("message"), ast.StringTerm(err.Error())), ) default: errObj = ast.NewObject( - ast.Item(ast.InternedTerm("code"), httpSendInternalErrTerm), + ast.Item(ast.InternedTerm("code"), ast.InternedTerm(HTTPSendInternalErr)), ast.Item(ast.InternedTerm("message"), ast.StringTerm(err.Error())), ) } @@ -178,7 +220,6 @@ func generateRaiseErrorResult(err error) *ast.Term { } func getHTTPResponse(bctx BuiltinContext, req ast.Object) (*ast.Term, error) { - bctx.Metrics.Timer(httpSendLatencyMetricKey).Start() defer bctx.Metrics.Timer(httpSendLatencyMetricKey).Stop() @@ -200,11 +241,12 @@ func getHTTPResponse(bctx BuiltinContext, req ast.Object) (*ast.Term, error) { if resp == nil { httpResp, err := reqExecutor.ExecuteHTTPRequest() + defer util.Close(httpResp) + if err != nil { reqExecutor.InsertErrorIntoCache(err) return nil, err } - defer util.Close(httpResp) // Add result to intra/inter-query cache. resp, err = reqExecutor.InsertIntoCache(httpResp) if err != nil { @@ -229,59 +271,47 @@ func getKeyFromRequest(req ast.Object) (ast.Object, error) { key.Insert(ast.InternedTerm("cache_ignored_headers"), ast.InternedNullTerm) return key, nil } - var cacheIgnoredHeaders []string - err := ast.As(cacheIgnoredHeadersTerm.Value, &cacheIgnoredHeaders) - if err != nil { - return nil, err - } - var allHeaders map[string]any - err = ast.As(allHeadersTerm.Value, &allHeaders) - if err != nil { - return nil, err + cacheIgnoredHeaders, ok := cacheIgnoredHeadersTerm.Value.(*ast.Array) + if !ok || cacheIgnoredHeaders.Until(util.Not(ast.TermValueIs[ast.String])) { + return nil, errors.New("cache_ignored_headers must be an array of strings") } - for _, header := range cacheIgnoredHeaders { - delete(allHeaders, header) - } - val, err := ast.InterfaceToValue(allHeaders) - if err != nil { - return nil, err - } - key.Insert(ast.InternedTerm("headers"), ast.NewTerm(val)) + allHeaders := allHeadersTerm.Value.(ast.Object) + filteredHeaders := ast.NewObjectWithCapacity(allHeaders.Len()) + + allHeaders.Foreach(func(key, val *ast.Term) { + if !cacheIgnoredHeaders.Until(key.Equal) { + filteredHeaders.Insert(key, val) + } + }) + + key.Insert(ast.InternedTerm("headers"), ast.NewTerm(filteredHeaders)) // remove cache_ignored_headers key key.Insert(ast.InternedTerm("cache_ignored_headers"), ast.InternedNullTerm) return key, nil } func init() { + ast.InternStringTerm(HTTPSendNetworkErr, HTTPSendInternalErr) + ast.InternStringTerm(allowedKeyNames[:]...) for _, element := range allowedKeyNames { - ast.InternStringTerm(element) allowedKeys.Add(ast.InternedTerm(element)) } - ast.InternStringTerm(HTTPSendNetworkErr, HTTPSendInternalErr) - httpSendNetworkErrTerm = ast.InternedTerm(HTTPSendNetworkErr) - httpSendInternalErrTerm = ast.InternedTerm(HTTPSendInternalErr) - createCacheableHTTPStatusCodes() initDefaults() RegisterBuiltinFunc(ast.HTTPSend.Name, builtinHTTPSend) } -func handleHTTPSendErr(bctx BuiltinContext, err error) error { +func handleHTTPSendErr(ctx context.Context, loc *ast.Location, err error) error { // Return HTTP client timeout errors in a generic error message to avoid confusion about what happened. // Do not do this if the builtin context was cancelled and is what caused the request to stop. - if urlErr, ok := err.(*url.Error); ok && urlErr.Timeout() && bctx.Context.Err() == nil { + if urlErr, ok := err.(*url.Error); ok && urlErr.Timeout() && ctx.Err() == nil { err = fmt.Errorf("%s %s: request timed out", urlErr.Op, urlErr.URL) } - if err := bctx.Context.Err(); err != nil { - return Halt{ - Err: &Error{ - Code: CancelErr, - Message: fmt.Sprintf("http.send: timed out (%s)", err.Error()), - }, - } + if err := ctx.Err(); err != nil { + return Halt{Err: &Error{Code: CancelErr, Message: fmt.Sprintf("http.send: timed out (%s)", err.Error())}} } - return handleBuiltinErr(ast.HTTPSend.Name, bctx.Location, err) + return handleBuiltinErr(ast.HTTPSend.Name, loc, err) } func initDefaults() { @@ -300,7 +330,6 @@ func initDefaults() { } func validateHTTPRequestOperand(term *ast.Term, pos int) (ast.Object, error) { - obj, err := builtins.ObjectOperand(term.Value, pos) if err != nil { return nil, err @@ -324,8 +353,7 @@ func validateHTTPRequestOperand(term *ast.Term, pos int) (ast.Object, error) { // canonicalizeHeaders returns a copy of the headers where the keys are in // canonical HTTP form. func canonicalizeHeaders(headers map[string]any) map[string]any { - canonicalized := map[string]any{} - + canonicalized := make(map[string]any, len(headers)) for k, v := range headers { canonicalized[http.CanonicalHeaderKey(k)] = v } @@ -371,21 +399,16 @@ func useSocket(rawURL string) (bool, string, *http.Transport) { return true, u.String(), tr } -func verifyHost(bctx BuiltinContext, host string) error { - if bctx.Capabilities == nil || bctx.Capabilities.AllowNet == nil { +func verifyHost(caps *ast.Capabilities, host string) error { + if caps == nil || caps.AllowNet == nil || slices.Contains(caps.AllowNet, host) { return nil } - - if slices.Contains(bctx.Capabilities.AllowNet, host) { - return nil - } - - return fmt.Errorf("unallowed host: %s", host) + return fmt.Errorf("disallowed host: %s", host) } -func verifyURLHost(bctx BuiltinContext, unverifiedURL string) error { +func verifyURLHost(caps *ast.Capabilities, unverifiedURL string) error { // Eager return to avoid unnecessary URL parsing - if bctx.Capabilities == nil || bctx.Capabilities.AllowNet == nil { + if caps == nil || caps.AllowNet == nil { return nil } @@ -394,47 +417,40 @@ func verifyURLHost(bctx BuiltinContext, unverifiedURL string) error { return err } - host := strings.Split(parsedURL.Host, ":")[0] + host, _, _ := strings.Cut(parsedURL.Host, ":") - return verifyHost(bctx, host) + return verifyHost(caps, host) } func createHTTPRequest(bctx BuiltinContext, obj ast.Object) (*http.Request, *http.Client, error) { var ( url, method string - // Additional CA certificates loading options. - tlsCaCert []byte - tlsCaCertEnvVar, tlsCaCertFile string - // Client TLS certificate and key options. Each input source - // comes in a matched pair. - tlsClientCert, tlsClientKey []byte - tlsClientCertEnvVar, tlsClientKeyEnvVar string - tlsClientCertFile, tlsClientKeyFile, tlsServerName string - - body, rawBody *bytes.Buffer - enableRedirect, tlsInsecureSkipVerify bool - tlsUseSystemCerts *bool - tlsConfig tls.Config + // CA and client certificates loading options. Each input source comes in a matched pair. + tlsCaCertEnvVar, tlsCaCertFile string + tlsCaCert, tlsClientCert, tlsClientKey []byte + tlsClientCertEnvVar, tlsClientKeyEnvVar, tlsClientCertFile, tlsClientKeyFile string + + body *bytes.Buffer + enableRedirect, tlsUseSystemCerts, ok bool customHeaders map[string]any ) + tlsConfig := &tls.Config{} timeout := defaultHTTPRequestTimeout - for _, val := range obj.Keys() { - key, err := ast.JSON(val.Value) + for _, key := range obj.Keys() { + keyAny, err := ast.JSON(key.Value) if err != nil { return nil, nil, err } - - key = key.(string) + val := obj.Get(key) var strVal string - - if s, ok := obj.Get(val).Value.(ast.String); ok { + if s, ok := val.Value.(ast.String); ok { strVal = strings.Trim(string(s), "\"") } else { // Most parameters are strings, so consolidate the type checking. - switch key { + switch keyAny { case "method", "url", "raw_body", @@ -448,83 +464,78 @@ func createHTTPRequest(bctx BuiltinContext, obj ast.Object) (*http.Request, *htt "tls_client_key_file", "tls_client_key_env_variable", "tls_server_name": - return nil, nil, fmt.Errorf("%q must be a string", key) + return nil, nil, fmt.Errorf("%q must be a string", keyAny) } } - switch key { + switch keyAny { case "method": method = strings.ToUpper(strVal) case "url": - err := verifyURLHost(bctx, strVal) - if err != nil { + if err := verifyURLHost(bctx.Capabilities, strVal); err != nil { return nil, nil, err } url = strVal case "enable_redirect": - enableRedirect, err = strconv.ParseBool(obj.Get(val).String()) - if err != nil { + if enableRedirect, err = strconv.ParseBool(val.String()); err != nil { return nil, nil, err } case "body": - bodyVal := obj.Get(val).Value - bodyValInterface, err := ast.JSON(bodyVal) + if body != nil { + break // raw_body takes precedence + } + bodyVal, err := ast.JSON(val.Value) if err != nil { return nil, nil, err } - - bodyValBytes, err := json.Marshal(bodyValInterface) + bodyValBytes, err := json.Marshal(bodyVal) if err != nil { return nil, nil, err } body = bytes.NewBuffer(bodyValBytes) case "raw_body": - rawBody = bytes.NewBufferString(strVal) + body = bytes.NewBufferString(strVal) case "tls_use_system_certs": - tempTLSUseSystemCerts, err := strconv.ParseBool(obj.Get(val).String()) + tlsUseSystemCerts, err = strconv.ParseBool(val.String()) if err != nil { return nil, nil, err } - tlsUseSystemCerts = &tempTLSUseSystemCerts case "tls_ca_cert": - tlsCaCert = []byte(strVal) + tlsCaCert = util.StringToByteSlice(strVal) case "tls_ca_cert_file": tlsCaCertFile = strVal case "tls_ca_cert_env_variable": tlsCaCertEnvVar = strVal case "tls_client_cert": - tlsClientCert = []byte(strVal) + tlsClientCert = util.StringToByteSlice(strVal) case "tls_client_cert_file": tlsClientCertFile = strVal case "tls_client_cert_env_variable": tlsClientCertEnvVar = strVal case "tls_client_key": - tlsClientKey = []byte(strVal) + tlsClientKey = util.StringToByteSlice(strVal) case "tls_client_key_file": tlsClientKeyFile = strVal case "tls_client_key_env_variable": tlsClientKeyEnvVar = strVal case "tls_server_name": - tlsServerName = strVal + tlsConfig.ServerName = strVal case "headers": - headersVal := obj.Get(val).Value - headersValInterface, err := ast.JSON(headersVal) + headersValInterface, err := ast.JSON(val.Value) if err != nil { return nil, nil, err } - var ok bool customHeaders, ok = headersValInterface.(map[string]any) if !ok { return nil, nil, errors.New("invalid type for headers key") } case "tls_insecure_skip_verify": - tlsInsecureSkipVerify, err = strconv.ParseBool(obj.Get(val).String()) + tlsConfig.InsecureSkipVerify, err = strconv.ParseBool(val.String()) if err != nil { return nil, nil, err } case "timeout": - timeout, err = parseTimeout(obj.Get(val).Value) - if err != nil { + if timeout, err = parseTimeout(val.Value); err != nil { return nil, nil, err } case "cache", "caching_mode", @@ -532,7 +543,7 @@ func createHTTPRequest(bctx BuiltinContext, obj ast.Object) (*http.Request, *htt "force_json_decode", "force_yaml_decode", "raise_error", "max_retry_attempts", "cache_ignored_headers": // no-op default: - return nil, nil, fmt.Errorf("invalid parameter %q", key) + return nil, nil, fmt.Errorf("invalid parameter %q", keyAny) } } @@ -540,26 +551,13 @@ func createHTTPRequest(bctx BuiltinContext, obj ast.Object) (*http.Request, *htt customHeaders = canonicalizeHeaders(customHeaders) } - isTLS := false - client := &http.Client{ - Timeout: timeout, - CheckRedirect: func(*http.Request, []*http.Request) error { - return http.ErrUseLastResponse - }, - } - - if tlsInsecureSkipVerify { - isTLS = true - tlsConfig.InsecureSkipVerify = tlsInsecureSkipVerify - } + client := &http.Client{Timeout: timeout, CheckRedirect: useLastResponseRedirect} if len(tlsClientCert) > 0 && len(tlsClientKey) > 0 { cert, err := tls.X509KeyPair(tlsClientCert, tlsClientKey) if err != nil { return nil, nil, err } - - isTLS = true tlsConfig.Certificates = append(tlsConfig.Certificates, cert) } @@ -568,8 +566,6 @@ func createHTTPRequest(bctx BuiltinContext, obj ast.Object) (*http.Request, *htt if err != nil { return nil, nil, err } - - isTLS = true tlsConfig.Certificates = append(tlsConfig.Certificates, cert) } @@ -581,20 +577,16 @@ func createHTTPRequest(bctx BuiltinContext, obj ast.Object) (*http.Request, *htt return nil, nil, fmt.Errorf("cannot extract public/private key pair from envvars %q, %q: %w", tlsClientCertEnvVar, tlsClientKeyEnvVar, err) } - - isTLS = true tlsConfig.Certificates = append(tlsConfig.Certificates, cert) } // Check the system certificates config first so that we // load additional certificated into the correct pool. - if tlsUseSystemCerts != nil && *tlsUseSystemCerts && runtime.GOOS != "windows" { + if tlsUseSystemCerts && runtime.GOOS != "windows" { pool, err := x509.SystemCertPool() if err != nil { return nil, nil, err } - - isTLS = true tlsConfig.RootCAs = pool } @@ -604,8 +596,6 @@ func createHTTPRequest(bctx BuiltinContext, obj ast.Object) (*http.Request, *htt if err != nil { return nil, nil, err } - - isTLS = true tlsConfig.RootCAs = pool } @@ -614,8 +604,6 @@ func createHTTPRequest(bctx BuiltinContext, obj ast.Object) (*http.Request, *htt if err != nil { return nil, nil, err } - - isTLS = true tlsConfig.RootCAs = pool } @@ -624,35 +612,27 @@ func createHTTPRequest(bctx BuiltinContext, obj ast.Object) (*http.Request, *htt if err != nil { return nil, nil, err } - - isTLS = true tlsConfig.RootCAs = pool } - // If Host header is set, use it for TLS server name. - if host, hasHost := customHeaders["Host"]; hasHost { + // If Host header is set, use it for TLS server name, unless set with tls_server_name. + if host, hasHost := customHeaders["Host"]; tlsConfig.ServerName == "" && hasHost { // Only default the ServerName if the caller has // specified the host. If we don't specify anything, // Go will default to the target hostname. This name // is not the same as the default that Go populates // `req.Host` with, which is why we don't just set // this unconditionally. - isTLS = true tlsConfig.ServerName, _ = host.(string) } - if tlsServerName != "" { - isTLS = true - tlsConfig.ServerName = tlsServerName - } - var transport *http.Transport if ok, parsedURL, tr := useSocket(url); ok { transport = tr url = parsedURL - } else if isTLS { + } else if hasTLSConfig(tlsConfig) { transport = http.DefaultTransport.(*http.Transport).Clone() - transport.TLSClientConfig = &tlsConfig + transport.TLSClientConfig = tlsConfig transport.DisableKeepAlives = true } @@ -665,25 +645,17 @@ func createHTTPRequest(bctx BuiltinContext, obj ast.Object) (*http.Request, *htt // check if redirects are enabled if enableRedirect { client.CheckRedirect = func(req *http.Request, _ []*http.Request) error { - return verifyURLHost(bctx, req.URL.String()) + return verifyURLHost(bctx.Capabilities, req.URL.String()) } } - if rawBody != nil { - body = rawBody - } else if body == nil { - body = bytes.NewBufferString("") - } - // create the http request, use the builtin context's context to ensure // the request is cancelled if evaluation is cancelled. - req, err := http.NewRequest(method, url, body) + req, err := http.NewRequestWithContext(bctx.Context, method, url, util.Or(body, emptyBytesBuffer)) if err != nil { return nil, nil, err } - req = req.WithContext(bctx.Context) - // Add custom headers if len(customHeaders) != 0 { for k, v := range customHeaders { @@ -691,7 +663,6 @@ func createHTTPRequest(bctx BuiltinContext, obj ast.Object) (*http.Request, *htt if !ok { return nil, nil, fmt.Errorf("invalid type for headers value %q", v) } - req.Header.Add(k, header) } @@ -703,8 +674,7 @@ func createHTTPRequest(bctx BuiltinContext, obj ast.Object) (*http.Request, *htt // If the caller specifies the Host header, use it for the HTTP // request host and the TLS server name. if host, hasHost := customHeaders["Host"]; hasHost { - host := host.(string) // We already checked that it's a string. - req.Host = host + req.Host = host.(string) // We already checked that it's a string. } } @@ -715,6 +685,17 @@ func createHTTPRequest(bctx BuiltinContext, obj ast.Object) (*http.Request, *htt return req, client, nil } +func hasTLSConfig(tlsConfig *tls.Config) bool { + return tlsConfig.InsecureSkipVerify || + tlsConfig.ServerName != "" || + tlsConfig.RootCAs != nil || + len(tlsConfig.Certificates) > 0 +} + +func useLastResponseRedirect(*http.Request, []*http.Request) error { + return http.ErrUseLastResponse +} + func executeHTTPRequest(req *http.Request, client *http.Client, inputReqObj ast.Object) (*http.Response, error) { var err error var retry int @@ -753,6 +734,10 @@ func executeHTTPRequest(req *http.Request, client *http.Client, inputReqObj ast. return nil, err } +func emptyBytesBuffer() *bytes.Buffer { + return bytes.NewBuffer([]byte{}) +} + func isJSONType(header http.Header) bool { t, _, err := mime.ParseMediaType(header.Get("Content-Type")) if err != nil { @@ -782,16 +767,6 @@ func isContentType(header http.Header, typ ...string) bool { return false } -type httpSendCacheEntry struct { - response *ast.Value - error error -} - -// The httpSendCache is used for intra-query caching of http.send results. -type httpSendCache struct { - entries *util.HasherMap[ast.Value, httpSendCacheEntry] -} - func newHTTPSendCache() *httpSendCache { return &httpSendCache{ entries: util.NewHasherMap[ast.Value, httpSendCacheEntry](ast.ValueEqual), @@ -883,7 +858,7 @@ func (c *interQueryCache) checkHTTPSendInterQueryCache() (ast.Value, error) { value, cerr := requestCache.Clone(cachedValue) if cerr != nil { - return nil, handleHTTPSendErr(c.bctx, cerr) + return nil, handleHTTPSendErr(c.bctx.Context, c.bctx.Location, cerr) } c.bctx.Metrics.Counter(httpSendInterQueryCacheHits).Incr() @@ -902,17 +877,20 @@ func (c *interQueryCache) checkHTTPSendInterQueryCache() (ast.Value, error) { return nil, nil } - if getCurrentTime(c.bctx).Before(cachedRespData.ExpiresAt) { + if getCurrentTime(c.bctx.Time).Before(cachedRespData.ExpiresAt) { return cachedRespData.formatToAST(c.forceJSONDecode, c.forceYAMLDecode) } var err error c.httpReq, c.httpClient, err = createHTTPRequest(c.bctx, c.key) if err != nil { - return nil, handleHTTPSendErr(c.bctx, err) + return nil, handleHTTPSendErr(c.bctx.Context, c.bctx.Location, err) } - headers := parseResponseHeaders(cachedRespData.Headers) + headers := &responseHeaders{ + etag: cachedRespData.Headers.Get("etag"), + lastModified: cachedRespData.Headers.Get("last-modified"), + } // check with the server if the stale response is still up-to-date. // If server returns a new response (ie. status_code=200), update the cache with the new response @@ -935,8 +913,8 @@ func (c *interQueryCache) checkHTTPSendInterQueryCache() (ast.Value, error) { } if forceCaching(c.forceCacheParams) { - createdAt := getCurrentTime(c.bctx) - cachedRespData.ExpiresAt = createdAt.Add(time.Second * time.Duration(c.forceCacheParams.forceCacheDurationSeconds)) + createdAt := getCurrentTime(c.bctx.Time) + cachedRespData.ExpiresAt = createdAt.Add(time.Second * time.Duration(c.forceCacheParams.forceDurationSeconds)) } else { expiresAt, err := expiryFromHeaders(result.Header) if err != nil { @@ -979,13 +957,17 @@ func (c *interQueryCache) checkHTTPSendInterQueryCache() (ast.Value, error) { } // insertIntoHTTPSendInterQueryCache inserts given key and value in the inter-query cache -func insertIntoHTTPSendInterQueryCache(bctx BuiltinContext, key ast.Value, resp *http.Response, respBody []byte, cacheParams *forceCacheParams) error { +func insertIntoHTTPSendInterQueryCache( + bctx BuiltinContext, + key ast.Value, + resp *http.Response, + respBody []byte, + cacheParams forceCacheParams, +) error { if resp == nil || (!forceCaching(cacheParams) && !canStore(resp.Header)) || !cacheableCodes.Contains(ast.InternedTerm(resp.StatusCode)) { return nil } - requestCache := bctx.InterQueryBuiltinCache - obj, ok := key.(ast.Object) if !ok { return errors.New("interface conversion error") @@ -999,9 +981,9 @@ func insertIntoHTTPSendInterQueryCache(bctx BuiltinContext, key ast.Value, resp var pcv cache.InterQueryCacheValue var pcvData *interQueryCacheData if cachingMode == defaultCachingMode { - pcv, pcvData, err = newInterQueryCacheValue(bctx, resp, respBody, cacheParams) + pcv, pcvData, err = newInterQueryCacheValue(bctx.Time, resp, respBody, cacheParams) } else { - pcvData, err = newInterQueryCacheData(bctx, resp, respBody, cacheParams) + pcvData, err = newInterQueryCacheData(bctx.Time, resp, respBody, cacheParams) pcv = pcvData } @@ -1009,7 +991,7 @@ func insertIntoHTTPSendInterQueryCache(bctx BuiltinContext, key ast.Value, resp return err } - requestCache.InsertWithExpiry(key, pcv, pcvData.ExpiresAt) + bctx.InterQueryBuiltinCache.InsertWithExpiry(key, pcv, pcvData.ExpiresAt) return nil } @@ -1019,32 +1001,26 @@ func createCacheableHTTPStatusCodes() { } } -func parseTimeout(timeoutVal ast.Value) (time.Duration, error) { - var timeout time.Duration +func parseTimeout(timeoutVal ast.Value) (timeout time.Duration, err error) { switch t := timeoutVal.(type) { case ast.Number: - timeoutInt, ok := t.Int64() - if !ok { - return timeout, fmt.Errorf("invalid timeout number value %v, must be int64", timeoutVal) + if timeoutInt, ok := t.Int64(); ok { + return time.Duration(timeoutInt), nil } - return time.Duration(timeoutInt), nil + err = fmt.Errorf("invalid timeout number value %v, must be int64", timeoutVal) case ast.String: // Support strings without a unit, treat them the same as just a number value (ns) - var err error - timeoutInt, err := strconv.ParseInt(string(t), 10, 64) - if err == nil { + if timeoutInt, ok := util.Atoi64(string(t)); ok { return time.Duration(timeoutInt), nil } - // Try parsing it as a duration (requires a supported units suffix) - timeout, err = time.ParseDuration(string(t)) - if err != nil { - return timeout, fmt.Errorf("invalid timeout value %v: %s", timeoutVal, err) + if timeout, err = time.ParseDuration(string(t)); err != nil { + err = fmt.Errorf("invalid timeout value %v: %s", timeoutVal, err) } - return timeout, nil default: - return timeout, builtins.NewOperandErr(1, "'timeout' must be one of {string, number} but got %s", ast.ValueName(t)) + err = builtins.NewOperandErr(1, "'timeout' must be one of {string, number} but got %s", ast.ValueName(t)) } + return timeout, err } func getBoolValFromReqObj(req ast.Object, key *ast.Term) (bool, error) { @@ -1094,12 +1070,13 @@ func getCachingMode(req ast.Object) (cachingMode, error) { return defaultCachingMode, nil } -type interQueryCacheValue struct { - Data []byte -} - -func newInterQueryCacheValue(bctx BuiltinContext, resp *http.Response, respBody []byte, cacheParams *forceCacheParams) (*interQueryCacheValue, *interQueryCacheData, error) { - data, err := newInterQueryCacheData(bctx, resp, respBody, cacheParams) +func newInterQueryCacheValue( + now *ast.Term, + resp *http.Response, + respBody []byte, + cacheParams forceCacheParams, +) (*interQueryCacheValue, *interQueryCacheData, error) { + data, err := newInterQueryCacheData(now, resp, respBody, cacheParams) if err != nil { return nil, nil, err } @@ -1112,34 +1089,20 @@ func newInterQueryCacheValue(bctx BuiltinContext, resp *http.Response, respBody } func (cb interQueryCacheValue) Clone() (cache.InterQueryCacheValue, error) { - dup := make([]byte, len(cb.Data)) - copy(dup, cb.Data) - return &interQueryCacheValue{Data: dup}, nil + return &interQueryCacheValue{Data: slices.Clone(cb.Data)}, nil } func (cb interQueryCacheValue) SizeInBytes() int64 { return int64(len(cb.Data)) } -func (cb *interQueryCacheValue) copyCacheData() (*interQueryCacheData, error) { - var res interQueryCacheData - err := util.UnmarshalJSON(cb.Data, &res) - if err != nil { - return nil, err - } - return &res, nil +func (cb *interQueryCacheValue) copyCacheData() (res *interQueryCacheData, err error) { + err = util.UnmarshalJSON(cb.Data, &res) + return res, err } -type interQueryCacheData struct { - RespBody []byte - Status string - StatusCode int - Headers http.Header - ExpiresAt time.Time -} - -func forceCaching(cacheParams *forceCacheParams) bool { - return cacheParams != nil && cacheParams.forceCacheDurationSeconds > 0 +func forceCaching(cacheParams forceCacheParams) bool { + return cacheParams.forceDurationSeconds > 0 } func expiryFromHeaders(headers http.Header) (time.Time, error) { @@ -1160,29 +1123,25 @@ func expiryFromHeaders(headers http.Header) (time.Time, error) { return expiresAt, nil } -func newInterQueryCacheData(bctx BuiltinContext, resp *http.Response, respBody []byte, cacheParams *forceCacheParams) (*interQueryCacheData, error) { - var expiresAt time.Time - - if forceCaching(cacheParams) { - createdAt := getCurrentTime(bctx) - expiresAt = createdAt.Add(time.Second * time.Duration(cacheParams.forceCacheDurationSeconds)) - } else { - var err error - expiresAt, err = expiryFromHeaders(resp.Header) - if err != nil { - return nil, err - } - } - - cv := interQueryCacheData{ - ExpiresAt: expiresAt, +func newInterQueryCacheData( + now *ast.Term, + resp *http.Response, + respBody []byte, + cacheParams forceCacheParams, +) (data *interQueryCacheData, err error) { + data = &interQueryCacheData{ RespBody: respBody, Status: resp.Status, StatusCode: resp.StatusCode, Headers: resp.Header, } + if forceCaching(cacheParams) { + data.ExpiresAt = getCurrentTime(now).Add(time.Second * time.Duration(cacheParams.forceDurationSeconds)) + } else { + data.ExpiresAt, err = expiryFromHeaders(resp.Header) + } - return &cv, nil + return data, err } func (c *interQueryCacheData) formatToAST(forceJSONDecode, forceYAMLDecode bool) (ast.Value, error) { @@ -1202,53 +1161,31 @@ func (*interQueryCacheData) SizeInBytes() int64 { } func (c *interQueryCacheData) Clone() (cache.InterQueryCacheValue, error) { - dup := make([]byte, len(c.RespBody)) - copy(dup, c.RespBody) - return &interQueryCacheData{ ExpiresAt: c.ExpiresAt, - RespBody: dup, + RespBody: slices.Clone(c.RespBody), Status: c.Status, StatusCode: c.StatusCode, Headers: c.Headers.Clone(), }, nil } -type responseHeaders struct { - etag string // identifier for a specific version of the response - lastModified string // date and time response was last modified as per origin server -} - -// deltaSeconds specifies a non-negative integer, representing -// time in seconds: http://tools.ietf.org/html/rfc7234#section-1.2.1 -type deltaSeconds int32 - -func parseResponseHeaders(headers http.Header) *responseHeaders { - result := responseHeaders{} - - result.etag = headers.Get("etag") - - result.lastModified = headers.Get("last-modified") - - return &result -} - -func revalidateCachedResponse(req *http.Request, client *http.Client, inputReqObj ast.Object, headers *responseHeaders) (*http.Response, bool, error) { - etag := headers.etag - lastModified := headers.lastModified - - if etag == "" && lastModified == "" { +func revalidateCachedResponse( + req *http.Request, + client *http.Client, + inputReqObj ast.Object, + headers *responseHeaders, +) (*http.Response, bool, error) { + if headers.etag == "" && headers.lastModified == "" { return nil, false, nil } cloneReq := req.Clone(req.Context()) - - if etag != "" { - cloneReq.Header.Set("if-none-match", etag) + if headers.etag != "" { + cloneReq.Header.Set("if-none-match", headers.etag) } - - if lastModified != "" { - cloneReq.Header.Set("if-modified-since", lastModified) + if headers.lastModified != "" { + cloneReq.Header.Set("if-modified-since", headers.lastModified) } response, err := executeHTTPRequest(cloneReq, client, inputReqObj) @@ -1256,49 +1193,29 @@ func revalidateCachedResponse(req *http.Request, client *http.Client, inputReqOb return nil, false, err } - switch response.StatusCode { - case http.StatusOK: - return response, true, nil - - case http.StatusNotModified: - return response, false, nil + if isOK := response.StatusCode == http.StatusOK; isOK || response.StatusCode == http.StatusNotModified { + return response, isOK, nil } + util.Close(response) return nil, false, nil } func canStore(headers http.Header) bool { - ccHeaders := parseCacheControlHeader(headers) - // Check "no-store" cache directive // The "no-store" response directive indicates that a cache MUST NOT // store any part of either the immediate request or response. - if _, ok := ccHeaders["no-store"]; ok { - return false - } - return true + _, ok := parseCacheControlHeader(headers)["no-store"] + return !ok } -func getCurrentTime(bctx BuiltinContext) time.Time { - var current time.Time - - value, err := ast.JSON(bctx.Time.Value) - if err != nil { - return current - } - - valueNum, ok := value.(json.Number) - if !ok { - return current - } - - valueNumInt, err := valueNum.Int64() - if err != nil { - return current +func getCurrentTime(now *ast.Term) time.Time { + if valueNum, ok := now.Value.(ast.Number); ok { + if valueNumInt, ok := valueNum.Int64(); ok { + return time.Unix(0, valueNumInt).UTC() + } } - - current = time.Unix(0, valueNumInt).UTC() - return current + return time.Now().UTC() } func parseCacheControlHeader(headers http.Header) map[string]string { @@ -1311,11 +1228,10 @@ func parseCacheControlHeader(headers http.Header) map[string]string { continue } if strings.ContainsRune(part, '=') { - items := strings.Split(part, "=") - if len(items) != 2 { - continue + if strings.Count(part, "=") == 1 { + left, right, _ := strings.Cut(part, "=") + ccDirectives[strings.Trim(left, " ")] = strings.Trim(right, ",") } - ccDirectives[strings.Trim(items[0], " ")] = strings.Trim(items[1], ",") } else { ccDirectives[part] = "" } @@ -1325,27 +1241,17 @@ func parseCacheControlHeader(headers http.Header) map[string]string { } func getResponseHeaderDate(headers http.Header) (date time.Time, err error) { - dateHeader := headers.Get("date") - if dateHeader == "" { - err = errors.New("no date header") - return + if dateHeader := headers.Get("date"); dateHeader != "" { + return http.ParseTime(dateHeader) } - return http.ParseTime(dateHeader) + return date, errors.New("no date header") } -func getResponseHeaderExpires(headers http.Header) time.Time { - expiresHeader := headers.Get("expires") - if expiresHeader == "" { - return time.Time{} +func getResponseHeaderExpires(headers http.Header) (exp time.Time) { + if expiresHeader := headers.Get("expires"); expiresHeader != "" { + exp, _ = http.ParseTime(expiresHeader) } - - date, err := http.ParseTime(expiresHeader) - if err != nil { - // servers can set `Expires: 0` which is an invalid date to indicate expired content - return time.Time{} - } - - return date + return exp } // parseMaxAgeCacheDirective parses the max-age directive expressed in delta-seconds as per @@ -1358,35 +1264,33 @@ func parseMaxAgeCacheDirective(cc map[string]string) (deltaSeconds, error) { val, err := strconv.ParseUint(maxAge, 10, 32) if err != nil { - if numError, ok := err.(*strconv.NumError); ok { - if numError.Err == strconv.ErrRange { - return deltaSeconds(math.MaxInt32), nil - } + if numError, ok := err.(*strconv.NumError); ok && numError.Err == strconv.ErrRange { + return deltaSeconds(math.MaxInt32), nil } return deltaSeconds(-1), err } - if val > math.MaxInt32 { - return deltaSeconds(math.MaxInt32), nil - } - return deltaSeconds(val), nil + return deltaSeconds(min(val, math.MaxInt32)), nil } func formatHTTPResponseToAST(resp *http.Response, forceJSONDecode, forceYAMLDecode bool) (ast.Value, []byte, error) { - resultRawBody, err := io.ReadAll(resp.Body) + raw, err := io.ReadAll(resp.Body) if err != nil { return nil, nil, err } - resultObj, err := prepareASTResult(resp.Header, forceJSONDecode, forceYAMLDecode, resultRawBody, resp.Status, resp.StatusCode) - if err != nil { - return nil, nil, err - } + resultObj, err := prepareASTResult(resp.Header, forceJSONDecode, forceYAMLDecode, raw, resp.Status, resp.StatusCode) - return resultObj, resultRawBody, nil + return resultObj, raw, err } -func prepareASTResult(headers http.Header, forceJSONDecode, forceYAMLDecode bool, body []byte, status string, statusCode int) (ast.Value, error) { +func prepareASTResult( + headers http.Header, + forceJSONDecode, forceYAMLDecode bool, + body []byte, + status string, + statusCode int, +) (ast.Value, error) { var resultBody any // If the response body cannot be JSON/YAML decoded, @@ -1399,39 +1303,25 @@ func prepareASTResult(headers http.Header, forceJSONDecode, forceYAMLDecode bool _ = util.Unmarshal(body, &resultBody) } - result := make(map[string]any) - result["status"] = status - result["status_code"] = statusCode - result["body"] = resultBody - result["raw_body"] = string(body) - result["headers"] = getResponseHeaders(headers) - - resultObj, err := ast.InterfaceToValue(result) + bodyValue, err := ast.InterfaceToValue(resultBody) if err != nil { return nil, err } - return resultObj, nil + return ast.NewObject( + ast.Item(ast.InternedTerm("status"), ast.InternedTerm(status)), + ast.Item(ast.InternedTerm("status_code"), ast.InternedTerm(statusCode)), + ast.Item(ast.InternedTerm("body"), ast.NewTerm(bodyValue)), + ast.Item(ast.InternedTerm("raw_body"), ast.InternedTerm(util.ByteSliceToString(body))), + ast.Item(ast.InternedTerm("headers"), getResponseHeaders(headers)), + ), nil } -func getResponseHeaders(headers http.Header) map[string]any { - respHeaders := map[string]any{} - for headerName, values := range headers { - var respValues []any - for _, v := range values { - respValues = append(respValues, v) - } - respHeaders[strings.ToLower(headerName)] = respValues +func getResponseHeaders(headers http.Header) *ast.Term { + if len(headers) == 0 { + return ast.InternedEmptyObject } - return respHeaders -} - -// httpRequestExecutor defines an interface for the http send cache -type httpRequestExecutor interface { - CheckCache() (ast.Value, error) - InsertIntoCache(value *http.Response) (ast.Value, error) - InsertErrorIntoCache(err error) - ExecuteHTTPRequest() (*http.Response, error) + return ast.NewTerm(ast.MapToObject(headers, strings.ToLower, arrayFromStringSlice)) } // newHTTPRequestExecutor returns a new HTTP request executor that wraps either an inter-query or @@ -1439,7 +1329,7 @@ type httpRequestExecutor interface { func newHTTPRequestExecutor(bctx BuiltinContext, req ast.Object, key ast.Object) (httpRequestExecutor, error) { useInterQueryCache, forceCacheParams, err := useInterQueryCache(req) if err != nil { - return nil, handleHTTPSendErr(bctx, err) + return nil, handleHTTPSendErr(bctx.Context, bctx.Location, err) } if useInterQueryCache && bctx.InterQueryBuiltinCache != nil { @@ -1448,41 +1338,22 @@ func newHTTPRequestExecutor(bctx BuiltinContext, req ast.Object, key ast.Object) return newIntraQueryCache(bctx, req, key) } -type interQueryCache struct { - bctx BuiltinContext - req ast.Object - key ast.Object - httpReq *http.Request - httpClient *http.Client - forceJSONDecode bool - forceYAMLDecode bool - forceCacheParams *forceCacheParams -} - -func newInterQueryCache(bctx BuiltinContext, req ast.Object, key ast.Object, forceCacheParams *forceCacheParams) (*interQueryCache, error) { +func newInterQueryCache(bctx BuiltinContext, req ast.Object, key ast.Object, forceCacheParams forceCacheParams) (*interQueryCache, error) { return &interQueryCache{bctx: bctx, req: req, key: key, forceCacheParams: forceCacheParams}, nil } // CheckCache checks the cache for the value of the key set on this object -func (c *interQueryCache) CheckCache() (ast.Value, error) { - var err error - +func (c *interQueryCache) CheckCache() (resp ast.Value, err error) { // Checking the intra-query cache first ensures consistency of errors and HTTP responses within a query. - resp, err := checkHTTPSendCache(c.bctx, c.key) - if err != nil { - return nil, err - } - if resp != nil { - return resp, nil + if resp, err = checkHTTPSendCache(c.bctx, c.key); err != nil || resp != nil { + return resp, err } - c.forceJSONDecode, err = getBoolValFromReqObj(c.key, ast.InternedTerm("force_json_decode")) - if err != nil { - return nil, handleHTTPSendErr(c.bctx, err) + if c.forceJSONDecode, err = getBoolValFromReqObj(c.key, ast.InternedTerm("force_json_decode")); err != nil { + return nil, handleHTTPSendErr(c.bctx.Context, c.bctx.Location, err) } - c.forceYAMLDecode, err = getBoolValFromReqObj(c.key, ast.InternedTerm("force_yaml_decode")) - if err != nil { - return nil, handleHTTPSendErr(c.bctx, err) + if c.forceYAMLDecode, err = getBoolValFromReqObj(c.key, ast.InternedTerm("force_yaml_decode")); err != nil { + return nil, handleHTTPSendErr(c.bctx.Context, c.bctx.Location, err) } resp, err = c.checkHTTPSendInterQueryCache() @@ -1500,7 +1371,7 @@ func (c *interQueryCache) CheckCache() (ast.Value, error) { func (c *interQueryCache) InsertIntoCache(value *http.Response) (ast.Value, error) { result, respBody, err := formatHTTPResponseToAST(value, c.forceJSONDecode, c.forceYAMLDecode) if err != nil { - return nil, handleHTTPSendErr(c.bctx, err) + return nil, handleHTTPSendErr(c.bctx.Context, c.bctx.Location, err) } // Always insert into the intra-query cache, to maintain consistency within the same query. @@ -1521,7 +1392,7 @@ func (c *interQueryCache) ExecuteHTTPRequest() (*http.Response, error) { var err error c.httpReq, c.httpClient, err = createHTTPRequest(c.bctx, c.req) if err != nil { - return nil, handleHTTPSendErr(c.bctx, err) + return nil, handleHTTPSendErr(c.bctx.Context, c.bctx.Location, err) } // Increment counter for actual network requests @@ -1530,12 +1401,6 @@ func (c *interQueryCache) ExecuteHTTPRequest() (*http.Response, error) { return executeHTTPRequest(c.httpReq, c.httpClient, c.req) } -type intraQueryCache struct { - bctx BuiltinContext - req ast.Object - key ast.Object -} - func newIntraQueryCache(bctx BuiltinContext, req ast.Object, key ast.Object) (*intraQueryCache, error) { return &intraQueryCache{bctx: bctx, req: req, key: key}, nil } @@ -1549,16 +1414,16 @@ func (c *intraQueryCache) CheckCache() (ast.Value, error) { func (c *intraQueryCache) InsertIntoCache(value *http.Response) (ast.Value, error) { forceJSONDecode, err := getBoolValFromReqObj(c.key, ast.InternedTerm("force_json_decode")) if err != nil { - return nil, handleHTTPSendErr(c.bctx, err) + return nil, handleHTTPSendErr(c.bctx.Context, c.bctx.Location, err) } forceYAMLDecode, err := getBoolValFromReqObj(c.key, ast.InternedTerm("force_yaml_decode")) if err != nil { - return nil, handleHTTPSendErr(c.bctx, err) + return nil, handleHTTPSendErr(c.bctx.Context, c.bctx.Location, err) } result, _, err := formatHTTPResponseToAST(value, forceJSONDecode, forceYAMLDecode) if err != nil { - return nil, handleHTTPSendErr(c.bctx, err) + return nil, handleHTTPSendErr(c.bctx.Context, c.bctx.Location, err) } if cacheableCodes.Contains(ast.InternedTerm(value.StatusCode)) { @@ -1576,7 +1441,7 @@ func (c *intraQueryCache) InsertErrorIntoCache(err error) { func (c *intraQueryCache) ExecuteHTTPRequest() (*http.Response, error) { httpReq, httpClient, err := createHTTPRequest(c.bctx, c.req) if err != nil { - return nil, handleHTTPSendErr(c.bctx, err) + return nil, handleHTTPSendErr(c.bctx.Context, c.bctx.Location, err) } // Increment counter for actual network requests @@ -1585,15 +1450,15 @@ func (c *intraQueryCache) ExecuteHTTPRequest() (*http.Response, error) { return executeHTTPRequest(httpReq, httpClient, c.req) } -func useInterQueryCache(req ast.Object) (bool, *forceCacheParams, error) { +func useInterQueryCache(req ast.Object) (bool, forceCacheParams, error) { value, err := getBoolValFromReqObj(req, ast.InternedTerm("cache")) if err != nil { - return false, nil, err + return false, forceCacheParams{}, err } valueForceCache, err := getBoolValFromReqObj(req, ast.InternedTerm("force_cache")) if err != nil { - return false, nil, err + return false, forceCacheParams{}, err } if valueForceCache { @@ -1601,27 +1466,23 @@ func useInterQueryCache(req ast.Object) (bool, *forceCacheParams, error) { return true, forceCacheParams, err } - return value, nil, nil + return value, forceCacheParams{}, nil } -type forceCacheParams struct { - forceCacheDurationSeconds int32 -} - -func newForceCacheParams(req ast.Object) (*forceCacheParams, error) { +func newForceCacheParams(req ast.Object) (p forceCacheParams, err error) { term := req.Get(ast.InternedTerm("force_cache_duration_seconds")) if term == nil { - return nil, errors.New("'force_cache' set but 'force_cache_duration_seconds' parameter is missing") + return p, errors.New("'force_cache' set but 'force_cache_duration_seconds' parameter is missing") } forceCacheDurationSeconds := term.String() value, err := strconv.ParseInt(forceCacheDurationSeconds, 10, 32) if err != nil { - return nil, err + return p, err } - return &forceCacheParams{forceCacheDurationSeconds: int32(value)}, nil + return forceCacheParams{forceDurationSeconds: int32(value)}, nil } func getRaiseErrorValue(req ast.Object) (bool, error) { @@ -1634,3 +1495,7 @@ func getRaiseErrorValue(req ast.Object) (bool, error) { } return bool(result), nil } + +func arrayFromStringSlice(slice []string) *ast.Term { + return ast.ArrayTerm(util.Map(slice, ast.InternedTerm)...) +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/json.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/json.go index aeccac825f..bb8cc4925f 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/json.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/json.go @@ -378,9 +378,7 @@ func builtinJSONPatch(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Ter } func init() { - for _, key := range []string{"op", "path", "from", "value", "add", "remove", "replace", "move", "copy", "test"} { - ast.InternStringTerm(key) - } + ast.InternStringTerm("op", "path", "from", "value", "add", "remove", "replace", "move", "copy", "test") RegisterBuiltinFunc(ast.JSONFilter.Name, builtinJSONFilter) RegisterBuiltinFunc(ast.JSONRemove.Name, builtinJSONRemove) diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/jsonschema.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/jsonschema.go index 0236e5be09..d6fa329473 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/jsonschema.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/jsonschema.go @@ -54,15 +54,26 @@ func newResultTerm(valid bool, data *ast.Term) *ast.Term { // type-checking path, where pattern validation is disabled to tolerate // schemas containing ECMA-262 regex features that Go's RE2 dialect can't // compile. -func newPatternValidatingSchemaLoader() *gojsonschema.SchemaLoader { +// +// Remote reference fetching is restricted to the hosts in the caller's +// allow_net capability. Schemas reaching these built-ins come from the policy +// or, worse, from input, so an unrestricted loader would let a `$ref` drive +// outbound requests from wherever OPA happens to be deployed. The query's +// context comes along so that those fetches are abandoned when evaluation is +// cancelled. +func newPatternValidatingSchemaLoader(bctx BuiltinContext) *gojsonschema.SchemaLoader { sl := gojsonschema.NewSchemaLoader() sl.ValidatePatterns = true + sl.Context = bctx.Context + if bctx.Capabilities != nil { + sl.AllowNet = bctx.Capabilities.AllowNet + } return sl } // builtinJSONSchemaVerify accepts 1 argument which can be string or object and checks if it is valid JSON schema. // Returns array [false, ] with error string at index 1, or [true, ""] with empty string at index 1 otherwise. -func builtinJSONSchemaVerify(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { +func builtinJSONSchemaVerify(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { // Take first argument and make JSON Loader from it. loader, err := astValueToJSONSchemaLoader(operands[0].Value) if err != nil { @@ -70,7 +81,7 @@ func builtinJSONSchemaVerify(_ BuiltinContext, operands []*ast.Term, iter func(* } // Check that schema is correct and parses without errors. - if _, err = newPatternValidatingSchemaLoader().Compile(loader); err != nil { + if _, err = newPatternValidatingSchemaLoader(bctx).Compile(loader); err != nil { return iter(newResultTerm(false, ast.StringTerm("jsonschema: "+err.Error()))) } @@ -80,6 +91,11 @@ func builtinJSONSchemaVerify(_ BuiltinContext, operands []*ast.Term, iter func(* // builtinJSONMatchSchema accepts 2 arguments both can be string or object and verifies if the document matches the JSON schema. // Returns an array where first element is a boolean indicating a successful match, and the second is an array of errors that is empty on success and populated on failure. // In case of internal error returns empty array. +// +// Cached schemas are keyed on the schema value alone. A compiled schema has +// already resolved its remote references, so a cache shared between callers +// with differing allow_net would leak across them. That needs a deliberately +// shared cache, an assumption http.send makes as well. func builtinJSONMatchSchema(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { var schema *gojsonschema.Schema @@ -106,7 +122,7 @@ func builtinJSONMatchSchema(bctx BuiltinContext, operands []*ast.Term, iter func return err } - schema, err = newPatternValidatingSchemaLoader().Compile(schemaLoader) + schema, err = newPatternValidatingSchemaLoader(bctx).Compile(schemaLoader) if err != nil { return err } diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/net.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/net.go index 6caa068b47..2245e06948 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/net.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/net.go @@ -24,7 +24,7 @@ func builtinLookupIPAddr(bctx BuiltinContext, operands []*ast.Term, iter func(*a } name := string(a) - err = verifyHost(bctx, name) + err = verifyHost(bctx.Capabilities, name) if err != nil { return err } diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/parse_bytes.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/parse_bytes.go index f912d276c2..73e2d940eb 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/parse_bytes.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/parse_bytes.go @@ -37,7 +37,7 @@ func parseNumBytesError(msg string) error { } func errBytesUnitNotRecognized(unit string) error { - return parseNumBytesError(fmt.Sprintf("byte unit %s not recognized", unit)) + return parseNumBytesError("byte unit " + unit + " not recognized") } var ( @@ -116,9 +116,7 @@ func builtinNumBytes(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term // Makes the string lower case and removes quotation marks func formatString(s ast.String) string { - str := string(s) - lower := strings.ToLower(str) - return strings.ReplaceAll(lower, "\"", "") + return strings.ReplaceAll(strings.ToLower(string(s)), "\"", "") } // Splits the string into a number string à la "10" or "10.2" and a unit diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/providers.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/providers.go index 29d721e4b2..511797ac8e 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/providers.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/providers.go @@ -203,11 +203,7 @@ func builtinAWSSigV4SignReq(_ BuiltinContext, operands []*ast.Term, iter func(*a } func init() { - for _, key := range []string{ - "aws_service", "aws_access_key", "aws_secret_access_key", "aws_region", "disable_payload_signing", - } { - ast.InternStringTerm(key) - } + ast.InternStringTerm("aws_service", "aws_access_key", "aws_secret_access_key", "aws_region", "disable_payload_signing") awsRequiredConfigKeyNames = ast.NewSet( ast.InternedTerm("aws_service"), diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/query.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/query.go index 971d62b33b..e912bbae59 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/query.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/query.go @@ -4,7 +4,7 @@ import ( "context" "crypto/rand" "io" - "sort" + "slices" "time" "github.com/open-policy-agent/opa/v1/ast" @@ -16,6 +16,7 @@ import ( "github.com/open-policy-agent/opa/v1/topdown/copypropagation" "github.com/open-policy-agent/opa/v1/topdown/print" "github.com/open-policy-agent/opa/v1/tracing" + "github.com/open-policy-agent/opa/v1/util" ) // QueryResultSet represents a collection of results returned by a query. @@ -281,9 +282,7 @@ func (q *Query) WithBuiltinErrorList(list *[]Error) *Query { // WithResolver configures an external resolver to use for the given ref. func (q *Query) WithResolver(ref ast.Ref, r resolver.Resolver) *Query { - if q.external == nil { - q.external = newResolverTrie() - } + q.external = util.Or(q.external, newResolverTrie) q.external.Put(ref, r) return q } @@ -378,9 +377,7 @@ func (q *Query) PartialRun(ctx context.Context) (partials []ast.Body, support [] if q.time.IsZero() { q.time = time.Now() } - if q.metrics == nil { - q.metrics = metrics.New() - } + q.metrics = util.Or(q.metrics, metrics.New) f := &queryIDFactory{} b := newBindings(0, q.instr) @@ -492,9 +489,7 @@ func (q *Query) PartialRun(ctx context.Context) (partials []ast.Body, support [] }) // cannot return error // Sort binding expressions so that results are deterministic. - sort.Slice(bindingExprs, func(i, j int) bool { - return bindingExprs[i].Compare(bindingExprs[j]) < 0 - }) + slices.SortFunc(bindingExprs, (*ast.Expr).Compare) for i := range bindingExprs { body.Append(bindingExprs[i]) @@ -541,10 +536,7 @@ func (q *Query) PartialRun(ctx context.Context) (partials []ast.Body, support [] if regoVersion := q.compiler.DefaultRegoVersion(); regoVersion != ast.RegoUndefined { ast.SetModuleRegoVersion(m, q.compiler.DefaultRegoVersion()) } - - sort.Slice(support[i].Rules, func(j, k int) bool { - return support[i].Rules[j].Compare(support[i].Rules[k]) < 0 - }) + slices.SortFunc(support[i].Rules, (*ast.Rule).Compare) } return partials, support, err @@ -581,9 +573,7 @@ func (q *Query) Iter(ctx context.Context, iter func(QueryResult) error) error { if q.time.IsZero() { q.time = time.Now() } - if q.metrics == nil { - q.metrics = metrics.New() - } + q.metrics = util.Or(q.metrics, metrics.New) f := &queryIDFactory{} diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/reachable.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/reachable.go index 683c31c6c6..3a3271e39e 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/reachable.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/reachable.go @@ -40,10 +40,12 @@ func builtinReachable(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Ter var queue []*ast.Term switch initial := operands[1].Value.(type) { - case *ast.Array, ast.Set: + case *ast.Array: foreachVertex(ast.NewTerm(initial), func(t *ast.Term) { queue = append(queue, t) }) + case ast.Set: + queue = append(queue, initial.Slice()...) default: return builtins.NewOperandTypeErr(2, initial, "{array, set}") } @@ -113,10 +115,12 @@ func builtinReachablePaths(_ BuiltinContext, operands []*ast.Term, iter func(*as // initialised to the initial set of nodes we start out with. var queue []*ast.Term switch initial := operands[1].Value.(type) { - case *ast.Array, ast.Set: + case *ast.Array: foreachVertex(ast.NewTerm(initial), func(t *ast.Term) { queue = append(queue, t) }) + case ast.Set: + queue = append(queue, initial.Slice()...) default: return builtins.NewOperandTypeErr(2, initial, "{array, set}") } diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/regex_template.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/regex_template.go index a1d946fd59..0c1f698bfe 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/regex_template.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/regex_template.go @@ -82,10 +82,7 @@ func compileRegexTemplate(tpl string, delimiterStart, delimiterEnd byte) (*regex return nil, errBraces } varsR := make([]*regexp.Regexp, len(idxs)/2) - pattern := bytes.NewBufferString("") - - // WriteByte's error value is always nil for bytes.Buffer, no need to check it. - pattern.WriteByte('^') + pattern := bytes.NewBufferString("^") var end int var err error @@ -113,10 +110,5 @@ func compileRegexTemplate(tpl string, delimiterStart, delimiterEnd byte) (*regex pattern.WriteByte('$') // Compile full regexp. - reg, errCompile := regexp.Compile(pattern.String()) - if errCompile != nil { - return nil, errCompile - } - - return reg, nil + return regexp.Compile(pattern.String()) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/save.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/save.go index a1c0fdd65a..30d250f9c6 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/save.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/save.go @@ -383,13 +383,17 @@ func saveRequired(compilerTree *ast.TreeNode, extStack *externalTreeStack, ic *i } else if ic.Disabled(v.ConstantPrefix(), icIgnoreInternal) { found = true } else { - rules := getRulesDynamic(compilerTree, extStack, v, ast.RulesOptions{IncludeHiddenModules: false}) - for _, rule := range rules { - if saveRequired(compilerTree, extStack, ic, icIgnoreInternal, ss, b, rule, true) { - found = true - break - } + // Only terms from the call site can be plugged: once traversal + // recurses into a rule, that rule's variables belong to another + // binding list and could resolve to unrelated values in b. + lookup := v + if !rec { + lookup = plugRefForRuleLookup(v, b) } + found = anyRuleDynamic(compilerTree, extStack, lookup, ast.RulesOptions{IncludeHiddenModules: false}, + func(rule *ast.Rule) bool { + return saveRequired(compilerTree, extStack, ic, icIgnoreInternal, ss, b, rule, true) + }) } } } @@ -401,10 +405,41 @@ func saveRequired(compilerTree *ast.TreeNode, extStack *externalTreeStack, ic *i return found } -// getRulesDynamic looks up rules in both the compiler tree and external sources. -func getRulesDynamic(compilerTree *ast.TreeNode, extStack *externalTreeStack, ref ast.Ref, opts ast.RulesOptions) []*ast.Rule { - var rules []*ast.Rule +// plugRefForRuleLookup replaces variables in ref that are bound to a scalar with +// that value, narrowing rule lookup to the sub-tree that will actually be +// evaluated. Positions left as-is, because they are unbound or bound to a +// composite, fan out over all children as before. +func plugRefForRuleLookup(ref ast.Ref, b *bindings) ast.Ref { + if b == nil { + return ref + } + + cpy := ref + + for i := 1; i < len(ref); i++ { + if _, ok := ref[i].Value.(ast.Var); !ok { + continue + } + plugged := b.Plug(ref[i]) + if !ast.IsScalar(plugged.Value) { + continue + } + if len(cpy) == len(ref) && &cpy[0] == &ref[0] { + cpy = make(ast.Ref, len(ref)) + copy(cpy, ref) + } + cpy[i] = plugged + } + return cpy +} + +// anyRuleDynamic invokes f for the rules matching ref in the external trees and +// the compiler tree, stopping as soon as f returns true. Rules are streamed to f +// rather than collected so that callers only interested in whether *some* rule +// satisfies a predicate don't pay for walking the whole matching sub-tree, which +// for refs with non-constant elements can mean every rule loaded. +func anyRuleDynamic(compilerTree *ast.TreeNode, extStack *externalTreeStack, ref ast.Ref, opts ast.RulesOptions, f func(*ast.Rule) bool) bool { // Check external trees if extStack != nil { for i := range extStack.entries { @@ -412,63 +447,75 @@ func getRulesDynamic(compilerTree *ast.TreeNode, extStack *externalTreeStack, re if entry.tree != nil && ref.HasPrefix(entry.ref) { // Navigate into the external tree using the remaining path remaining := ref[len(entry.ref):] - rules = append(rules, getRulesFromTree(entry.tree, remaining, opts)...) + if anyRuleFromTree(entry.tree, remaining, opts, f) { + return true + } } } } // Then check compiler tree - rules = append(rules, getRulesFromTree(compilerTree, ref, opts)...) - - return rules + return anyRuleFromTree(compilerTree, ref, opts, f) } -// getRulesFromTree walks a tree to find all rules matching the given ref. -func getRulesFromTree(node *ast.TreeNode, ref ast.Ref, opts ast.RulesOptions) []*ast.Rule { - set := map[*ast.Rule]struct{}{} - var walk func(*ast.TreeNode, int) - walk = func(nav *ast.TreeNode, i int) { +// anyRuleFromTree walks a tree to find rules matching the given ref, invoking f +// for each and stopping early if f returns true. +func anyRuleFromTree(node *ast.TreeNode, ref ast.Ref, opts ast.RulesOptions, f func(*ast.Rule) bool) bool { + var walk func(*ast.TreeNode, int) bool + walk = func(nav *ast.TreeNode, i int) bool { switch { case i >= len(ref): - nav.DepthFirst(func(descendant *ast.TreeNode) bool { - for _, rule := range descendant.Values { - set[rule] = struct{}{} - } - if opts.IncludeHiddenModules { - return false - } - return descendant.Hide - }) + // The rules on nav itself have already been passed to f by the caller, + // unless nav is where the walk started. + return anyRuleDescendant(nav, opts, f, i == 0) case i == 0 || ast.IsConstant(ref[i].Value): - if child := nav.Child(ref[i].Value); child != nil { - for _, rule := range child.Values { - set[rule] = struct{}{} - } - walk(child, i+1) - } else { - return + child := nav.Child(ref[i].Value) + if child == nil { + return false } + return anyRule(child.Values, f) || walk(child, i+1) default: for _, child := range nav.Children { if child.Hide && !opts.IncludeHiddenModules { continue } - for _, rule := range child.Values { - set[rule] = struct{}{} + if anyRule(child.Values, f) || walk(child, i+1) { + return true } - walk(child, i+1) } + return false } } - walk(node, 0) - rules := make([]*ast.Rule, 0, len(set)) - for rule := range set { - rules = append(rules, rule) + return walk(node, 0) +} + +// anyRuleDescendant invokes f for every rule in node's sub-tree, stopping early +// if f returns true. The rules on node itself are only visited if visitSelf is +// set. Hidden nodes are not descended into unless opts.IncludeHiddenModules is +// set. +func anyRuleDescendant(node *ast.TreeNode, opts ast.RulesOptions, f func(*ast.Rule) bool, visitSelf bool) bool { + if visitSelf && anyRule(node.Values, f) { + return true + } + + if node.Hide && !opts.IncludeHiddenModules { + return false + } + + for _, child := range node.Children { + if anyRuleDescendant(child, opts, f, true) { + return true + } } - return rules + + return false +} + +func anyRule(rules []*ast.Rule, f func(*ast.Rule) bool) bool { + return slices.ContainsFunc(rules, f) } func ignoreExprDuringPartial(expr *ast.Expr) bool { diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/sets.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/sets.go index 6ee467efc8..e92f5882b2 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/sets.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/sets.go @@ -11,7 +11,6 @@ import ( // Deprecated: deprecated in v0.4.2 in favour of minus/infix "-" operation. func builtinSetDiff(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - s1, err := builtins.SetOperand(operands[0].Value, 1) if err != nil { return err @@ -27,7 +26,6 @@ func builtinSetDiff(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) // builtinSetIntersection returns the intersection of the given input sets func builtinSetIntersection(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - inputSet, err := builtins.SetOperand(operands[0].Value, 1) if err != nil { return err @@ -40,8 +38,8 @@ func builtinSetIntersection(_ BuiltinContext, operands []*ast.Term, iter func(*a var result ast.Set - err = inputSet.Iter(func(x *ast.Term) error { - n, err := builtins.SetOperand(x.Value, 1) + for _, term := range inputSet.Slice() { + n, err := builtins.SetOperand(term.Value, 1) if err != nil { return err } @@ -51,10 +49,6 @@ func builtinSetIntersection(_ BuiltinContext, operands []*ast.Term, iter func(*a } else { result = result.Intersect(n) } - return nil - }) - if err != nil { - return err } return iter(ast.NewTerm(result)) } @@ -72,31 +66,22 @@ func builtinSetUnion(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term // First pass: count total elements for pre-allocation totalSize := 0 - err = inputSet.Iter(func(x *ast.Term) error { - item, err := builtins.SetOperand(x.Value, 1) + for _, term := range inputSet.Slice() { + item, err := builtins.SetOperand(term.Value, 1) if err != nil { return err } totalSize += item.Len() - return nil - }) - if err != nil { - return err } // Pre-allocate result set with estimated capacity - result := ast.NewSetWithCapacity(totalSize) - - err = inputSet.Iter(func(x *ast.Term) error { - item, _ := builtins.SetOperand(x.Value, 1) // error checked above - item.Foreach(result.Add) - return nil - }) - if err != nil { - return err + terms := make([]*ast.Term, 0, totalSize) + for _, term := range inputSet.Slice() { + item, _ := builtins.SetOperand(term.Value, 1) // error checked above + terms = append(terms, item.Slice()...) } - return iter(ast.NewTerm(result)) + return iter(ast.SetTerm(terms...)) } func init() { diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/strings.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/strings.go index 4ffd307c78..69495cc8bb 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/strings.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/strings.go @@ -20,6 +20,11 @@ import ( "github.com/open-policy-agent/opa/v1/util" ) +var ( + trueAny any = true + errEmptySearchCharacter = errors.New("empty search character") +) + func builtinAnyPrefixMatch(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { a, b := operands[0].Value, operands[1].Value @@ -102,13 +107,17 @@ func anyStartsWithAny(strs []string, prefixes []string) bool { return strings.HasPrefix(strs[0], prefixes[0]) } + // The trie is local, and only ever inserted into and searched, so it's safe + // to hand it byte slices aliasing the operand strings' memory. Note that + // patricia's compact() writes through the key slices it retains, so Delete + // and DeleteSubtree must not be used here: they'd corrupt those strings. trie := patricia.NewTrie() for i := range strs { - trie.Insert([]byte(strs[i]), true) + trie.Insert(util.StringToByteSlice(strs[i]), trueAny) } for i := range prefixes { - if trie.MatchSubtree([]byte(prefixes[i])) { + if trie.MatchSubtree(util.StringToByteSlice(prefixes[i])) { return true } } @@ -312,12 +321,12 @@ func builtinIndexOf(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) return err } if len(string(search)) == 0 { - return errors.New("empty search character") + return errEmptySearchCharacter } if isASCII(string(base)) && isASCII(string(search)) { - // this is a false positive in the indexAlloc rule that thinks - // we're converting byte arrays to strings + // this is a false positive in the indexAlloc rule that thinks we're converting + // byte arrays to strings. still a false positive as of 2026-08-19. //nolint:gocritic return iter(ast.InternedTerm(strings.Index(string(base), string(search)))) } @@ -350,7 +359,7 @@ func builtinIndexOfN(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term return err } if len(string(search)) == 0 { - return errors.New("empty search character") + return errEmptySearchCharacter } baseRunes := []rune(string(base)) @@ -372,7 +381,6 @@ func builtinIndexOfN(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term } func builtinSubstring(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - base, err := builtins.StringOperand(operands[0].Value, 1) if err != nil { return err @@ -583,24 +591,14 @@ func builtinSplitN(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) limit = -1 } parts := strings.SplitN(text, delim, limit) - end := n - if end > len(parts) { - end = len(parts) - } - result = make([]*ast.Term, end) + result = make([]*ast.Term, min(n, len(parts))) for i := range result { result[i] = ast.InternedTerm(parts[i]) } } else { parts := strings.Split(text, delim) - start := len(parts) + n - if start < 0 { - start = 0 - } - result = make([]*ast.Term, len(parts)-start) - for i, p := range parts[start:] { - result[i] = ast.InternedTerm(p) - } + start := max(len(parts)+n, 0) + result = util.Map(parts[start:], ast.InternedTerm) } return iter(ast.ArrayTerm(result...)) @@ -864,7 +862,7 @@ func reverseString(str string) string { utf8.EncodeRune(buf[size-start:], r) } - return string(buf) + return util.ByteSliceToString(buf) } func init() { diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/test.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/test.go index 02958d2264..e3287b0eaa 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/test.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/test.go @@ -13,7 +13,8 @@ func builtinTestCase(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.T Op: TestCaseOp, QueryID: bctx.QueryID, Node: ast.NewExpr([]*ast.Term{ - ast.NewTerm(ast.InternalTestCase.Ref()), + // Copied, as tracers may transform the node. + ast.NewTerm(ast.Interned.Refs.InternalTestCase.Copy()), ast.NewTerm(operands[0].Value), }), } diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/time.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/time.go index 7171d5c0b2..b41904712f 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/time.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/time.go @@ -21,20 +21,22 @@ import ( "github.com/open-policy-agent/opa/v1/topdown/durationparser" ) -var tzCache map[string]*time.Location -var tzCacheMutex *sync.Mutex +var ( + tzCache = make(map[string]*time.Location) + tzCacheMutex = &sync.Mutex{} -// 1677-09-21T00:12:43.145224192-00:00 -var minDateAllowedForNsConversion = time.Unix(0, math.MinInt64) + // 1677-09-21T00:12:43.145224192-00:00 + minDateAllowedForNsConversion = time.Unix(0, math.MinInt64) -// 2262-04-11T23:47:16.854775807-00:00 -var maxDateAllowedForNsConversion = time.Unix(0, math.MaxInt64) + // 2262-04-11T23:47:16.854775807-00:00 + maxDateAllowedForNsConversion = time.Unix(0, math.MaxInt64) -var durationCoefficients = map[string]int{ - "d": 24, - "w": 7 * 24, - "y": 365 * 24, -} + durationCoefficients = map[string]int{ + "d": 24, + "w": 7 * 24, + "y": 365 * 24, + } +) // parseExtendedDuration parses a duration string that may contain extended // units (d, w, y) mixed with standard Go duration units (h, m, s, ms, us, ns). @@ -46,10 +48,7 @@ func parseExtendedDuration(s string) (int64, error) { if !strings.ContainsAny(s, "dwy") { v, err := time.ParseDuration(s) - if err != nil { - return 0, err - } - return int64(v), nil + return int64(v), err } result, err := durationparser.Parse("", []byte(s)) @@ -420,6 +419,4 @@ func init() { RegisterBuiltinFunc(ast.Weekday.Name, builtinWeekday) RegisterBuiltinFunc(ast.AddDate.Name, builtinAddDate) RegisterBuiltinFunc(ast.Diff.Name, builtinDiff) - tzCacheMutex = &sync.Mutex{} - tzCache = make(map[string]*time.Location) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/trace.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/trace.go index 8b672da54e..ae6191f5e2 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/trace.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/trace.go @@ -8,6 +8,7 @@ import ( "bytes" "fmt" "io" + "maps" "slices" "strings" @@ -15,6 +16,7 @@ import ( "github.com/open-policy-agent/opa/v1/ast" "github.com/open-policy-agent/opa/v1/topdown/builtins" + "github.com/open-policy-agent/opa/v1/util" ) const ( @@ -292,7 +294,7 @@ func (t *traceTable) write(w io.Writer, padding int) { if i < len(row)-1 { _, _ = fmt.Fprintf(w, "%-*s ", width, cell) } else { - _, _ = fmt.Fprintf(w, "%s", cell) + _, _ = w.Write(util.StringToByteSlice(cell)) } } _, _ = fmt.Fprintln(w) @@ -306,14 +308,14 @@ func PrettyTraceWithOpts(w io.Writer, trace []*Event, opts PrettyTraceOptions) { filePathAliases, _ := getShortenedFileNames(trace) table := traceTable{} + buf := new(bytes.Buffer) for _, event := range trace { depth := depths.GetOrSet(event.QueryID, event.ParentID) row := traceRow{} if opts.Locations { - location := formatLocation(event, filePathAliases) - row.add(location) + row.add(formatLocation(event, filePathAliases)) } row.add(formatEvent(event, depth)) @@ -322,31 +324,47 @@ func PrettyTraceWithOpts(w io.Writer, trace []*Event, opts PrettyTraceOptions) { vars := exprLocalVars(event) keys := sortedKeys(vars) - buf := new(bytes.Buffer) - buf.WriteString("{") - for i, k := range keys { - if i > 0 { + buf.Reset() + buf.WriteByte('{') + + if len(keys) > 0 { + k := keys[0] + buf.WriteString(k.String()) + buf.WriteString(": ") + buf.WriteString(iStrs.Truncate(vars.Get(k).String(), maxExprVarWidth)) + + for _, k := range keys[1:] { buf.WriteString(", ") + buf.WriteString(k.String()) + buf.WriteString(": ") + buf.WriteString(iStrs.Truncate(vars.Get(k).String(), maxExprVarWidth)) } - _, _ = fmt.Fprintf(buf, "%v: %s", k, iStrs.Truncate(vars.Get(k).String(), maxExprVarWidth)) } - buf.WriteString("}") + + buf.WriteByte('}') row.add(buf.String()) } if opts.LocalVariables { - if locals := event.Locals; locals != nil { + if locals := event.Locals; locals.Len() > 0 { keys := sortedKeys(locals) - buf := new(bytes.Buffer) - buf.WriteString("{") - for i, k := range keys { - if i > 0 { - buf.WriteString(", ") - } - _, _ = fmt.Fprintf(buf, "%v: %s", k, iStrs.Truncate(locals.Get(k).String(), maxExprVarWidth)) + buf.Reset() + buf.WriteByte('{') + + k := keys[0] + buf.WriteString(k.String()) + buf.WriteString(": ") + buf.WriteString(iStrs.Truncate(locals.Get(k).String(), maxExprVarWidth)) + + for _, k := range keys[1:] { + buf.WriteString(", ") + buf.WriteString(k.String()) + buf.WriteString(": ") + buf.WriteString(iStrs.Truncate(locals.Get(k).String(), maxExprVarWidth)) } - buf.WriteString("}") + + buf.WriteByte('}') row.add(buf.String()) } else { row.add("{}") @@ -365,21 +383,18 @@ func sortedKeys(vm *ast.ValueMap) []ast.Value { keys = append(keys, k) return false }) - slices.SortFunc(keys, func(a, b ast.Value) int { + return util.SortedFunc(keys, func(a, b ast.Value) int { return strings.Compare(a.String(), b.String()) }) - return keys } func exprLocalVars(e *Event) *ast.ValueMap { vars := ast.NewValueMap() - findVars := func(term *ast.Term) bool { - if name, ok := term.Value.(ast.Var); ok { - if meta, ok := e.LocalMetadata[name]; ok { - if val := e.Locals.Get(name); val != nil { - vars.Put(meta.Name, val) - } + findVars := func(name ast.Var) bool { + if meta, ok := e.LocalMetadata[name]; ok { + if val := e.Locals.Get(name); val != nil { + vars.Put(meta.Name, val) } } return false @@ -387,7 +402,7 @@ func exprLocalVars(e *Event) *ast.ValueMap { if r, ok := e.Node.(*ast.Rule); ok { // We're only interested in vars in the head, not the body - ast.WalkTerms(r.Head, findVars) + ast.WalkVars(r.Head, findVars) return vars } @@ -398,43 +413,47 @@ func exprLocalVars(e *Event) *ast.ValueMap { return false }) - ast.WalkTerms(e.Node, findVars) + ast.WalkVars(e.Node, findVars) return vars } func formatEvent(event *Event, depth int) string { - padding := formatEventPadding(event, depth) + buf := new(bytes.Buffer) + formatEventPaddingAppend(buf, event, depth) + buf.WriteString(string(event.Op)) + buf.WriteByte(' ') + if event.Op == NoteOp { - return fmt.Sprintf("%v%v %q", padding, event.Op, event.Message) + buf.WriteByte('"') + buf.WriteString(event.Message) + buf.WriteByte('"') + + return buf.String() } - var details any if node, ok := event.Node.(*ast.Rule); ok { - details = ast.RulePath(node) + bs, _ := node.Ref().ConstantPrefix().AppendText(buf.AvailableBuffer()) + buf.Write(bs) } else if event.Ref != nil { - details = event.Ref + bs, _ := event.Ref.AppendText(buf.AvailableBuffer()) + buf.Write(bs) } else { - details = rewrite(event).Node + fmt.Fprint(buf, rewrite(event).Node) } - template := "%v%v %v" - opts := []any{padding, event.Op, details} - if event.Message != "" { - template += " %v" - opts = append(opts, event.Message) + buf.WriteByte(' ') + buf.WriteString(event.Message) } - return fmt.Sprintf(template, opts...) + return buf.String() } -func formatEventPadding(event *Event, depth int) string { - spaces := formatEventSpaces(event, depth) - if spaces > 1 { - return strings.Repeat("| ", spaces-1) +func formatEventPaddingAppend(buf *bytes.Buffer, event *Event, depth int) { + for range formatEventSpaces(event, depth) - 1 { + buf.WriteString("| ") } - return "" } func formatEventSpaces(event *Event, depth int) int { @@ -461,11 +480,7 @@ func getShortenedFileNames(trace []*Event) (map[string]string, int) { if event.Location != nil { if event.Location.File != "" { // length of ":" - curLen := len(event.Location.File) + numDigits10(event.Location.Row) + 1 - if curLen > longestLocation { - longestLocation = curLen - } - + longestLocation = max(longestLocation, event.Location.StringLength()) if _, ok := fpAliases[event.Location.File]; ok { continue } @@ -476,10 +491,7 @@ func getShortenedFileNames(trace []*Event) (map[string]string, int) { fpAliases[event.Location.File] = event.Location.File } else { // length of ":" - curLen := minLocationWidth + numDigits10(event.Location.Row) + 1 - if curLen > longestLocation { - longestLocation = curLen - } + longestLocation = max(longestLocation, minLocationWidth+util.NumDigitsInt(event.Location.Row)+1) } } } @@ -491,25 +503,16 @@ func getShortenedFileNames(trace []*Event) (map[string]string, int) { return fpAliases, longestLocation } -func numDigits10(n int) int { - if n < 10 { - return 1 - } - return numDigits10(n/10) + 1 -} - func formatLocation(event *Event, fileAliases map[string]string) string { - - location := event.Location - if location == nil { + if event.Location == nil { return "" } - if location.File == "" { - return fmt.Sprintf("query:%v", location.Row) + if event.Location.File == "" { + return fmt.Sprintf("query:%v", event.Location.Row) } - return fmt.Sprintf("%v:%v", fileAliases[location.File], location.Row) + return fmt.Sprintf("%v:%v", fileAliases[event.Location.File], event.Location.Row) } // depths is a helper for computing the depth of an event. Events within the @@ -528,7 +531,6 @@ func (ds depths) GetOrSet(qid uint64, pqid uint64) int { } func builtinTrace(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - str, err := builtins.StringOperand(operands[0].Value, 1) if err != nil { return handleBuiltinErr(ast.Trace.Name, bctx.Location, err) @@ -839,13 +841,13 @@ func PrettyEvent(w io.Writer, e *Event, opts PrettyEventOpts) error { } printPrettyVars(buf, exprVars) - _, _ = fmt.Fprint(w, buf.String()) + w.Write(buf.Bytes()) return nil } func printPrettyVars(w *bytes.Buffer, exprVars map[string]varInfo) { containsTabs := false - varRows := make(map[int]any) + varRows := make(map[int]any, len(exprVars)) for _, info := range exprVars { if len(info.exprLoc.Tabs) > 0 { containsTabs = true @@ -856,15 +858,9 @@ func printPrettyVars(w *bytes.Buffer, exprVars map[string]varInfo) { if containsTabs && len(varRows) > 1 { // We can't (currently) reliably point to var locations when they are on different rows that contain tabs. // So we'll just print them in alphabetical order instead. - byName := make([]varInfo, 0, len(exprVars)) - for _, info := range exprVars { - byName = append(byName, info) - } - slices.SortStableFunc(byName, func(a, b varInfo) int { - return strings.Compare(a.Title(), b.Title()) - }) - w.WriteString("\n\nWhere:\n") + + byName := slices.SortedStableFunc(maps.Values(exprVars), cmpVarInfoTitle) for _, info := range byName { fmt.Fprintf(w, "\n%s: %s", info.Title(), iStrs.Truncate(info.Value(), maxPrettyExprVarWidth)) } @@ -891,14 +887,18 @@ func printPrettyVars(w *bytes.Buffer, exprVars map[string]varInfo) { return } - w.WriteString("\n") + w.WriteByte('\n') printArrows(w, byCol, -1) - for i := len(byCol) - 1; i >= 0; i-- { - w.WriteString("\n") + for i := range slices.Backward(byCol) { + w.WriteByte('\n') printArrows(w, byCol, i) } } +func cmpVarInfoTitle(a, b varInfo) int { + return strings.Compare(a.Title(), b.Title()) +} + func printArrows(w *bytes.Buffer, l []varInfo, printValueAt int) { prevCol := 0 var slice []varInfo @@ -909,7 +909,6 @@ func printArrows(w *bytes.Buffer, l []varInfo, printValueAt int) { } isFirst := true for i, info := range slice { - isLast := i >= len(slice)-1 col := info.col @@ -924,26 +923,25 @@ func printArrows(w *bytes.Buffer, l []varInfo, printValueAt int) { } for j := range spaces { - tab := false + var space byte = ' ' if slices.Contains(info.exprLoc.Tabs, j+prevCol+1) { - w.WriteString("\t") - tab = true - } - if !tab { - w.WriteString(" ") + space = '\t' } + w.WriteByte(space) } if isLast && printValueAt >= 0 { valueStr := iStrs.Truncate(info.Value(), maxPrettyExprVarWidth) if (i > 0 && col == l[i-1].col) || (i < len(l)-1 && col == l[i+1].col) { // There is another var on this column, so we need to include the name to differentiate them. - fmt.Fprintf(w, "%s: %s", info.Title(), valueStr) + w.WriteString(info.Title()) + w.WriteString(": ") + w.WriteString(valueStr) } else { w.WriteString(valueStr) } } else { - w.WriteString("|") + w.WriteByte('|') } prevCol = col isFirst = false diff --git a/vendor/github.com/open-policy-agent/opa/v1/topdown/uuid.go b/vendor/github.com/open-policy-agent/opa/v1/topdown/uuid.go index 141fb908bd..87229f0785 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/topdown/uuid.go +++ b/vendor/github.com/open-policy-agent/opa/v1/topdown/uuid.go @@ -10,12 +10,10 @@ import ( "github.com/open-policy-agent/opa/v1/topdown/builtins" ) -type uuidCachingKey string +type uuidCachingKey int func builtinUUIDRFC4122(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { - - var key = uuidCachingKey(operands[0].Value.String()) - + key := uuidCachingKey(operands[0].Value.Hash()) val, ok := bctx.Cache.Get(key) if ok { return iter(val.(*ast.Term)) @@ -51,6 +49,11 @@ func builtinUUIDParse(_ BuiltinContext, operands []*ast.Term, iter func(term *as } func init() { + ast.InternStringTerm( + "version", "variant", "nodeid", "macvariables", "time", "clocksequence", "domain", "id", + "local:multicast", "global:multicast", "local:unicast", "global:unicast", "RFC4122", + "Person", "Group", "Org", + ) RegisterBuiltinFunc(ast.UUIDRFC4122.Name, builtinUUIDRFC4122) RegisterBuiltinFunc(ast.UUIDParse.Name, builtinUUIDParse) } diff --git a/vendor/github.com/open-policy-agent/opa/v1/types/types.go b/vendor/github.com/open-policy-agent/opa/v1/types/types.go index aff4fde39e..c02fee4d40 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/types/types.go +++ b/vendor/github.com/open-policy-agent/opa/v1/types/types.go @@ -11,7 +11,6 @@ import ( "errors" "fmt" "slices" - "sort" "strings" "github.com/open-policy-agent/opa/v1/util" @@ -144,11 +143,8 @@ func NewBoolean() Boolean { } // MarshalJSON returns the JSON encoding of t. -func (t Boolean) MarshalJSON() ([]byte, error) { - repr := map[string]any{ - "type": t.typeMarker(), - } - return json.Marshal(repr) +func (Boolean) MarshalJSON() ([]byte, error) { + return util.StringToByteSlice(`{"type":"boolean"}`), nil } func (t Boolean) String() string { @@ -164,10 +160,8 @@ func NewString() String { } // MarshalJSON returns the JSON encoding of t. -func (t String) MarshalJSON() ([]byte, error) { - return json.Marshal(map[string]any{ - "type": t.typeMarker(), - }) +func (String) MarshalJSON() ([]byte, error) { + return util.StringToByteSlice(`{"type":"string"}`), nil } func (String) String() string { @@ -183,10 +177,8 @@ func NewNumber() Number { } // MarshalJSON returns the JSON encoding of t. -func (t Number) MarshalJSON() ([]byte, error) { - return json.Marshal(map[string]any{ - "type": t.typeMarker(), - }) +func (Number) MarshalJSON() ([]byte, error) { + return util.StringToByteSlice(`{"type":"number"}`), nil } func (Number) String() string { @@ -296,8 +288,7 @@ func (t *Set) toMap() map[string]any { } func (t *Set) String() string { - prefix := typeSet - return prefix + "[" + Sprint(t.of) + "]" + return typeSet + "[" + Sprint(t.of) + "]" } // StaticProperty represents a static object property. @@ -345,7 +336,7 @@ func (p *DynamicProperty) MarshalJSON() ([]byte, error) { } func (p *DynamicProperty) String() string { - return fmt.Sprintf("%s: %s", Sprint(p.Key), Sprint(p.Value)) + return Sprint(p.Key) + ": " + Sprint(p.Value) } // Object represents the object type. @@ -356,11 +347,8 @@ type Object struct { // NewObject returns a new Object type. func NewObject(static []*StaticProperty, dynamic *DynamicProperty) *Object { - slices.SortFunc(static, func(a, b *StaticProperty) int { - return util.Compare(a.Key, b.Key) - }) return &Object{ - static: static, + static: util.SortedFunc(static, cmpSpKey), dynamic: dynamic, } } @@ -428,18 +416,12 @@ func (t *Object) toMap() map[string]any { // Select returns the type of the named property. func (t *Object) Select(name any) Type { - pos := sort.Search(len(t.static), func(x int) bool { - return util.Compare(t.static[x].Key, name) >= 0 - }) - - if pos < len(t.static) && util.Compare(t.static[pos].Key, name) == 0 { + if pos, found := slices.BinarySearchFunc(t.static, name, cmpSpKeyName); found { return t.static[pos].Value } - if t.dynamic != nil { - if Contains(t.dynamic.Key, TypeOf(name)) { - return t.dynamic.Value - } + if t.dynamic != nil && Contains(t.dynamic.Key, TypeOf(name)) { + return t.dynamic.Value } return nil @@ -553,8 +535,7 @@ type Any []Type func NewAny(of ...Type) Any { sl := make(Any, len(of)) copy(sl, of) - sort.Sort(typeSlice(sl)) - return sl + return util.SortedFunc(sl, Compare) } // Contains returns true if t is a superset of other. @@ -562,16 +543,8 @@ func (t Any) Contains(other Type) bool { if _, ok := other.(*Function); ok { return false } - // Note(philipc): We used to do this as a linear search. - // Since this is always sorted, we can use a binary search instead. - i := sort.Search(len(t), func(i int) bool { - return Compare(t[i], other) >= 0 - }) - if i < len(t) && Compare(t[i], other) == 0 { - // x is present at t[i] - return true - } - return len(t) == 0 + _, found := slices.BinarySearchFunc(t, other, Compare) + return found || len(t) == 0 } // MarshalJSON returns the JSON encoding of t. @@ -598,9 +571,8 @@ func (t Any) Merge(other Type) Any { return t } cpy := make(Any, len(t)+1) - idx := sort.Search(len(t), func(i int) bool { - return Compare(t[i], other) >= 0 - }) + idx, _ := slices.BinarySearchFunc(t, other, Compare) + copy(cpy, t[:idx]) cpy[idx] = other copy(cpy[idx+1:], t[idx:]) @@ -910,7 +882,7 @@ func Compare(a, b Type) int { return cmp } } - return typeSliceCompare(arrA.static, arrB.static) + return slices.CompareFunc(arrA.static, arrB.static, Compare) case *Object: objA := a.(*Object) objB := b.(*Object) @@ -964,9 +936,7 @@ func Compare(a, b Type) int { } return Compare(setA.of, setB.of) case Any: - sl1 := typeSlice(a.(Any)) - sl2 := typeSlice(b.(Any)) - return typeSliceCompare(sl1, sl2) + return slices.CompareFunc([]Type(a.(Any)), []Type(b.(Any)), Compare) case *Function: fA := a.(*Function) fB := b.(*Function) @@ -1212,36 +1182,11 @@ func TypeOf(x any) Type { } return NewObject(static, nil) case []any: - static := make([]Type, len(x)) - for i := range x { - static[i] = TypeOf(x[i]) - } - return NewArray(static, nil) + return NewArray(util.Map(x, TypeOf), nil) } panic("unreachable") } -type typeSlice []Type - -func (s typeSlice) Less(i, j int) bool { return Compare(s[i], s[j]) < 0 } -func (s typeSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s typeSlice) Len() int { return len(s) } - -func typeSliceCompare(a, b []Type) int { - minLen := min(len(b), len(a)) - for i := range minLen { - if cmp := Compare(a[i], b[i]); cmp != 0 { - return cmp - } - } - if len(a) < len(b) { - return -1 - } else if len(b) < len(a) { - return 1 - } - return 0 -} - func typeOrder(x Type) int { switch unwrapRecursive(unwrap(x)).(type) { case Null: @@ -1267,3 +1212,11 @@ func typeOrder(x Type) int { } panic("unreachable") } + +func cmpSpKeyName(p *StaticProperty, name any) int { + return util.Compare(p.Key, name) +} + +func cmpSpKey(a, b *StaticProperty) int { + return util.Compare(a.Key, b.Key) +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/util/compare.go b/vendor/github.com/open-policy-agent/opa/v1/util/compare.go index ec12210c6e..d9e6702d85 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/util/compare.go +++ b/vendor/github.com/open-policy-agent/opa/v1/util/compare.go @@ -35,6 +35,17 @@ func Or[T comparable](val T, suppliers ...func() T) T { return val } +// NilOr returns the first non-nil value from the provided list of pointers, or nil if all are nil. +func NilOr[T any](vals ...*T) *T { + for _, val := range vals { + if val != nil { + return val + } + } + + return nil +} + // SliceLenCompare is a convenience function for comparing / sorting // slices by their length using the various slices.SortX functions. func SliceLenCompare[T any, S ~[]T](a, b S) int { @@ -164,6 +175,15 @@ func Compare(a, b any) int { } func compareJSONNumber(a, b json.Number) int { + if a == b { + return 0 + } + if ai, ok := Atoi(string(a)); ok { + if bi, ok := Atoi(string(b)); ok { + return ai - bi + } + return -1 + } bigA, ok := new(big.Float).SetString(string(a)) if !ok { panic("illegal value") diff --git a/vendor/github.com/open-policy-agent/opa/v1/util/constraints.go b/vendor/github.com/open-policy-agent/opa/v1/util/constraints.go new file mode 100644 index 0000000000..3ad837f07f --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/v1/util/constraints.go @@ -0,0 +1,19 @@ +package util + +type ( + Number interface { + Integer | Float + } + Integer interface { + SignedInteger | UnsignedInteger + } + SignedInteger interface { + ~int | ~int8 | ~int16 | ~int32 | ~int64 + } + UnsignedInteger interface { + ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr + } + Float interface { + ~float32 | ~float64 + } +) diff --git a/vendor/github.com/open-policy-agent/opa/v1/util/maps.go b/vendor/github.com/open-policy-agent/opa/v1/util/maps.go index c56fbe98ac..b56d57a5f3 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/util/maps.go +++ b/vendor/github.com/open-policy-agent/opa/v1/util/maps.go @@ -2,7 +2,6 @@ package util import ( "cmp" - "slices" ) // Keys returns a slice of keys from any map. @@ -16,12 +15,7 @@ func Keys[M ~map[K]V, K comparable, V any](m M) []K { // KeysSorted returns a slice of keys from any map, sorted in ascending order. func KeysSorted[M ~map[K]V, K cmp.Ordered, V any](m M) []K { - r := make([]K, 0, len(m)) - for k := range m { - r = append(r, k) - } - slices.Sort(r) - return r + return Sorted(Keys(m)) } // Values returns a slice of values from any map. Copied from golang.org/x/exp/maps. diff --git a/vendor/github.com/open-policy-agent/opa/v1/util/performance.go b/vendor/github.com/open-policy-agent/opa/v1/util/performance.go index f269a2e1d7..553a4ec113 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/util/performance.go +++ b/vendor/github.com/open-policy-agent/opa/v1/util/performance.go @@ -1,6 +1,10 @@ package util import ( + "bytes" + "cmp" + "encoding" + "io" "slices" "strconv" "strings" @@ -13,12 +17,12 @@ import ( // put into the pool, and [SyncPool.Get] returns a pointer to T without having // to do a type assertion at the call site. type SyncPool[T any] struct { - pool sync.Pool + Pool sync.Pool } func NewSyncPool[T any]() *SyncPool[T] { return &SyncPool[T]{ - pool: sync.Pool{ + Pool: sync.Pool{ New: func() any { return new(T) }, @@ -27,12 +31,12 @@ func NewSyncPool[T any]() *SyncPool[T] { } func (p *SyncPool[T]) Get() *T { - return p.pool.Get().(*T) + return p.Pool.Get().(*T) } func (p *SyncPool[T]) Put(x *T) { if x != nil { - p.pool.Put(x) + p.Pool.Put(x) } } @@ -101,8 +105,15 @@ func ByteSliceToString(bs []byte) string { } // Allocation free conversion from ~string to []byte (unsafe) -// Note that the byte slice must not be modified after conversion +// Note that the byte slice must not be modified after conversion, and that it +// aliases the string's memory: it is a view of s, not a copy like []byte(s). func StringToByteSlice[T ~string](s T) []byte { + if len(s) == 0 { + // unsafe.StringData's return value is unspecified for the empty string, + // so don't build a slice on top of it. Doing so currently yields a nil + // slice, which callers may treat differently from an empty one. + return []byte{} + } return unsafe.Slice(unsafe.StringData(string(s)), len(s)) } @@ -147,10 +158,32 @@ func NumDigitsUint(n uint64) int { } // AppendInt is a less messy version of strconv.AppendInt for base 10 ints. -func AppendInt(buf []byte, n int) []byte { +func AppendInt[T Integer](buf []byte, n T) []byte { return strconv.AppendInt(buf, int64(n), 10) } +// WriteInt writes the string form of n to out. +func WriteInt[T Integer](out io.Writer, n T) (int, error) { + var buf []byte + if b, ok := out.(*bytes.Buffer); ok { + buf = b.AvailableBuffer() + } + return out.Write(AppendInt(buf, n)) +} + +// WriteAppender writes the appended text of appender to out. +func WriteAppender[T encoding.TextAppender](out io.Writer, appender T) (int, error) { + var buf []byte + if b, ok := out.(*bytes.Buffer); ok { + buf = b.AvailableBuffer() + } + b, err := appender.AppendText(buf) + if err != nil { + return 0, err + } + return out.Write(b) +} + // Atoi is a convenience function for [Atoi64] where an int is preferable to an int64. // See the documentation of [Atoi64] for details on the performance benefits of this // function over strconv.Atoi. @@ -168,6 +201,8 @@ func Atoi(s string) (int, bool) { // codebase — most notably ast.Number's Int() and Int64() methods — have no interest in the // details of the failure, and keeping this allocation free means both methods can be used // not only for conversion, but as a most efficient "IsInt64" check. +// Additionally this function accepts trailing decimal zeroes ("10.00", not "10.01") as that +// makes sense in the context of us using JSON numbers. func Atoi64(s string) (int64, bool) { sLen := len(s) if sLen > 0 { @@ -180,9 +215,23 @@ func Atoi64(s string) (int64, bool) { return 0, false } + var pastDecimal bool var n int64 for _, ch := range []byte(s) { + if ch == '.' { + if !pastDecimal { + pastDecimal = true + continue + } + return 0, false + } ch -= '0' + if pastDecimal { + if ch == 0 { + continue + } + return 0, false + } if ch > 9 { return 0, false } @@ -205,7 +254,7 @@ func Atoi64(s string) (int64, bool) { // SplitMap calls fn for each delim-separated part of text and returns a slice of the results. // Cheaper than calling fn on strings.Split(text, delim), as it avoids allocating an intermediate slice of strings. -func SplitMap[T any](text string, delim string, fn func(string) T) []T { +func SplitMap[T any](text, delim string, fn func(string) T) []T { sl := make([]T, 0, strings.Count(text, delim)+1) for s := range strings.SplitSeq(text, delim) { sl = append(sl, fn(s)) @@ -265,3 +314,15 @@ func SortedFunc[T any, S ~[]T](s S, cmp func(a, b T) int) S { slices.SortFunc(s, cmp) return s } + +// SortedStableFunc is simply a shorthand for [slices.SortStableFunc] which also returns the sorted slice. +func SortedStableFunc[T any, S ~[]T](s S, cmp func(a, b T) int) S { + slices.SortStableFunc(s, cmp) + return s +} + +// Sorted is simply a shorthand for [slices.Sort] which also returns the sorted slice. +func Sorted[T cmp.Ordered, S ~[]T](s S) S { + slices.Sort(s) + return s +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/util/slices.go b/vendor/github.com/open-policy-agent/opa/v1/util/slices.go index 57163a5649..21864ea5be 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/util/slices.go +++ b/vendor/github.com/open-policy-agent/opa/v1/util/slices.go @@ -4,6 +4,9 @@ package util +import "slices" + +// Map applies f to each element of s and returns a new slice containing the results. func Map[T any, U any](s []T, f func(T) U) []U { if s == nil { return nil @@ -14,3 +17,74 @@ func Map[T any, U any](s []T, f func(T) U) []U { } return r } + +// MapAppend applies f to each element of src and appends the results to dst, returning the resulting slice. +func MapAppend[T any, U any](dst []U, src []T, f func(T) U) []U { + if len(src) > 0 { + dst = slices.Grow(dst, len(src)) + for _, v := range src { + dst = append(dst, f(v)) + } + } + return dst +} + +// Every returns true if pred is true for every element of a, otherwise false. +// Returns true for empty / nil slices. +func Every[T any, S ~[]T](a S, pred func(T) bool) bool { + for _, v := range a { + if !pred(v) { + return false + } + } + return true +} + +// TryMap returns a new slice of type U by applying the function f to each element of the input slice s. +// If f returns an error for any element, the function aborts and returns the error. +func TryMap[T any, U any](s []T, f func(T) (U, error)) (r []U, err error) { + if s == nil { + return nil, nil + } + r = make([]U, len(s)) + for i, v := range s { + if r[i], err = f(v); err != nil { + return nil, err + } + } + return r, nil +} + +// ToSliceOfAny converts a slice of any type T to []any. +func ToSliceOfAny[T any](s []T) []any { + if s == nil { + return nil + } + r := make([]any, len(s)) + for i, v := range s { + r[i] = v + } + return r +} + +// Count returns the number of elements in items that satisfy pred. +func Count[T any](pred func(T) bool, items ...T) (c int) { + for i := range items { + if pred(items[i]) { + c++ + } + } + return c +} + +// Not returns a new predicate function that negates the result of pred. +func Not[T any](pred func(T) bool) func(T) bool { + return func(v T) bool { + return !pred(v) + } +} + +// Identity returns the input value unchanged. +func Identity[T any](v T) T { + return v +} diff --git a/vendor/github.com/open-policy-agent/opa/v1/version/version.go b/vendor/github.com/open-policy-agent/opa/v1/version/version.go index cb30f722fc..6befa5a594 100644 --- a/vendor/github.com/open-policy-agent/opa/v1/version/version.go +++ b/vendor/github.com/open-policy-agent/opa/v1/version/version.go @@ -10,7 +10,7 @@ import ( "runtime/debug" ) -var Version = "1.19.0" +var Version = "1.20.1" // GoVersion is the version of Go this was built with var GoVersion = runtime.Version() diff --git a/vendor/modules.txt b/vendor/modules.txt index 3ffd216d77..2100a33052 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -369,8 +369,8 @@ github.com/moby/sys/userns # github.com/mrunalp/fileutils v0.5.1 ## explicit; go 1.13 github.com/mrunalp/fileutils -# github.com/open-policy-agent/opa v1.19.0 -## explicit; go 1.25.0 +# github.com/open-policy-agent/opa v1.20.1 +## explicit; go 1.26.0 github.com/open-policy-agent/opa/capabilities github.com/open-policy-agent/opa/internal/bundle github.com/open-policy-agent/opa/internal/cidr/merge From a80527d846dadfb9cdcf2dd34f824ec22d00353c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 3 Sep 2026 07:02:51 +0000 Subject: [PATCH 2/2] go mod tidy && go mod vendor Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- test/go.mod | 2 +- test/go.sum | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test/go.mod b/test/go.mod index fb5c72b411..3f0c2bf7e7 100644 --- a/test/go.mod +++ b/test/go.mod @@ -86,7 +86,7 @@ require ( github.com/moby/sys/user v0.4.0 // indirect github.com/moby/sys/userns v0.1.0 // indirect github.com/mrunalp/fileutils v0.5.1 // indirect - github.com/open-policy-agent/opa v1.19.0 // indirect + github.com/open-policy-agent/opa v1.20.1 // indirect github.com/opencontainers/cgroups v0.1.0 // indirect github.com/opencontainers/runc v1.4.3 // indirect github.com/opencontainers/selinux v1.13.1 // indirect diff --git a/test/go.sum b/test/go.sum index 4c404eb720..2ed947443c 100644 --- a/test/go.sum +++ b/test/go.sum @@ -63,8 +63,8 @@ github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 h1:5RVFMOWjMyRy8cARdy79nAmgYw3hK/4HUq48LQ6Wwqo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= -github.com/dgraph-io/badger/v4 v4.9.4 h1:bcw+waCpzRZ2nmcSPbnPvDVhiEsn98TKmvnAhK7r7LM= -github.com/dgraph-io/badger/v4 v4.9.4/go.mod h1:nJjaJTUOSsQEBhsq209FmwCvMJzEA3e74RjZw6V2pQI= +github.com/dgraph-io/badger/v4 v4.9.5 h1:zT46OMrF3ntqsfI3ynKp7hUkQrGlcK2CX5psQmH0iW0= +github.com/dgraph-io/badger/v4 v4.9.5/go.mod h1:Xa9dAupjbwAacupWFCpa6YEn9E1PjBXkfZYr2I/8aWg= github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM= github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= @@ -177,8 +177,8 @@ github.com/mrunalp/fileutils v0.5.1 h1:F+S7ZlNKnrwHfSwdlgNSkKo67ReVf8o9fel6C3dkm github.com/mrunalp/fileutils v0.5.1/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/open-policy-agent/opa v1.19.0 h1:+j2OCsjMezZEML2T1lI9giJdGJS/PL1XFKgkHPGIhpo= -github.com/open-policy-agent/opa v1.19.0/go.mod h1:pb6Y6klyf7X7X8uXNDflruA9dQC2gMqWROXI5w/kvv0= +github.com/open-policy-agent/opa v1.20.1 h1:wSC3zjHbRyt7X3daV/DsjnhDywzB3l0m0gWhgX1W2vQ= +github.com/open-policy-agent/opa v1.20.1/go.mod h1:pxxSP1noAirD8UJ7PgAjoRw39IE0Bk/JRFkUP3+51lU= github.com/opencontainers/cgroups v0.1.0 h1:6W05KiDvgj1yi/6D13SJCvYx8TAESz67szvHPxhfcrs= github.com/opencontainers/cgroups v0.1.0/go.mod h1:hPBRvnBhLZueEN0eJyozMeM3HeFGYlZW9KnO//px6G4= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= @@ -198,12 +198,12 @@ github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.24.0 h1:5XStIklKuAtJSNpdD3s8XJj/Yv78IQmE1kbNk87JrAI= -github.com/prometheus/client_golang v1.24.0/go.mod h1:QcsNdotprC2nS4BTM2ucbcqxd2CeXTEa9jW7zHO9iDE= +github.com/prometheus/client_golang v1.24.1 h1:JnJkREXzWxUdCuPFpIWZiPispT9xVV59uiuyR2bPlnU= +github.com/prometheus/client_golang v1.24.1/go.mod h1:F+oSRECHg4sse5ucfYpYDeIv/hu68Zo0uoHKetWnzcE= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.70.0 h1:bcpru3tWPVnxGnETLgOV5jbp/JRXgYEyv65CuBLAMMI= -github.com/prometheus/common v0.70.0/go.mod h1:S/SFasQmgGiYH6C81LKCtYa8QACgthGg5zxL2udV7SY= +github.com/prometheus/common v0.70.1 h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi/PY= +github.com/prometheus/common v0.70.1/go.mod h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc= github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI= github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY= github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg=