From 239801c7195738cd86e32e911d40018b75f3f675 Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Thu, 25 Jul 2024 16:36:37 +0000 Subject: [PATCH 01/23] adding scoped enforcement actions Signed-off-by: Jaydip Gabani --- cmd/gator/test/gatortest_test.go | 1 + cmd/gator/test/test.go | 5 + go.mod | 15 +- go.sum | 32 +- main.go | 10 + pkg/audit/manager.go | 83 +- pkg/audit/manager_test.go | 26 +- .../constraint/constraint_controller.go | 42 +- .../constraint/constraint_controller_test.go | 251 + .../constraint/stats_reporter_test.go | 2 + .../constrainttemplate_controller_test.go | 116 +- pkg/fakes/fixtures.go | 46 + pkg/gator/client.go | 4 +- pkg/gator/fixtures/fixtures.go | 29 + pkg/gator/opa.go | 3 +- pkg/gator/test/test.go | 8 +- pkg/gator/test/test_test.go | 53 + pkg/gator/verify/runner.go | 5 +- pkg/logging/logging.go | 53 +- pkg/target/target_integration_test.go | 11 +- pkg/util/enforcement_action.go | 120 +- pkg/util/enforcement_action_test.go | 296 +- pkg/webhook/policy.go | 128 +- pkg/webhook/policy_test.go | 3 +- test/bats/test.bats | 101 + test/bats/tests/bad/bad_cm_audit.yaml | 40 + test/bats/tests/bad/bad_cm_scoped.yaml | 7 + .../all_cm_must_have_gatekeeper_scoped.yaml | 25 + ..._cm_must_have_gatekeeper_scoped_audit.yaml | 22 + ...m_must_have_gatekeeper_scoped_webhook.yaml | 22 + ...have_label_provided_vapbinding_scoped.yaml | 20 + test/bats/tests/good/no_dupe_cm.yaml | 9 + test/bats/tests/good/playground_ns.yaml | 5 + .../verify/constraint_with_scopedEA.yaml | 19 + ...traint_with_scopedEA_without_gator_ep.yaml | 16 + test/gator/verify/suite.yaml | 24 + vendor/github.com/cespare/xxhash/v2/README.md | 2 + vendor/github.com/cespare/xxhash/v2/xxhash.go | 29 +- .../cespare/xxhash/v2/xxhash_asm.go | 2 +- .../cespare/xxhash/v2/xxhash_other.go | 2 +- .../cespare/xxhash/v2/xxhash_safe.go | 2 +- .../cespare/xxhash/v2/xxhash_unsafe.go | 2 +- .../archive/compression/compression.go | 2 +- .../containerd/containerd/content/helpers.go | 4 +- .../containerd/content/local/locks.go | 2 +- .../containerd/content/local/readerat.go | 2 +- .../containerd/content/local/store.go | 4 +- .../containerd/content/local/writer.go | 4 +- .../containerd/errdefs/errdefs_deprecated.go | 116 + .../containerd/containerd/filters/filter.go | 2 +- .../containerd/containerd/filters/parser.go | 2 +- .../containerd/containerd/images/diffid.go | 2 +- .../containerd/containerd/images/handlers.go | 2 +- .../containerd/containerd/images/image.go | 7 +- .../containerd/images/mediatypes.go | 4 +- .../containerd/containerd/labels/validate.go | 2 +- .../containerd/platforms/cpuinfo.go | 2 +- .../containerd/platforms/cpuinfo_linux.go | 2 +- .../containerd/platforms/cpuinfo_other.go | 2 +- .../containerd/platforms/platforms.go | 2 +- .../containerd/remotes/docker/auth/fetch.go | 2 +- .../containerd/remotes/docker/authorizer.go | 22 +- .../containerd/remotes/docker/converter.go | 2 +- .../remotes/docker/converter_fuzz.go | 2 +- .../containerd/remotes/docker/fetcher.go | 4 +- .../containerd/remotes/docker/handler.go | 2 +- .../remotes/docker/httpreadseeker.go | 4 +- .../containerd/remotes/docker/pusher.go | 4 +- .../containerd/remotes/docker/resolver.go | 74 +- .../remotes/docker/schema1/converter.go | 4 +- .../containerd/remotes/docker/status.go | 2 +- .../containerd/containerd/remotes/handlers.go | 4 +- .../containerd/containerd/version/version.go | 2 +- .../constraint => containerd/errdefs}/LICENSE | 19 +- .../github.com/containerd/errdefs/README.md | 13 + .../{containerd => }/errdefs/errors.go | 0 .../{containerd => }/errdefs/grpc.go | 0 .../frameworks/constraint/deploy/crds.yaml | 45 +- .../frameworks/constraint/deploy/tools.go | 1 + .../constraint/pkg/apis/constraints/apis.go | 117 + .../constraint/pkg/client/client.go | 77 +- .../constraint/pkg/client/client_opts.go | 11 + .../pkg/client/constraint_client.go | 62 +- .../constraint/pkg/client/crds/schema.go | 23 + .../pkg/client/drivers/interface.go | 3 +- .../pkg/client/drivers/k8scel/driver.go | 20 +- .../k8scel/transform/make_vap_objects.go | 30 +- .../pkg/client/drivers/rego/driver.go | 9 +- .../pkg/client/drivers/to_result.go | 13 +- .../constraint/pkg/client/new_client.go | 7 +- .../query_opts.go => reviews/review_opts.go} | 25 +- .../constraint/pkg/client/template_client.go | 51 +- .../pkg/regorewriter/regorewriter.go | 2 +- .../constraint/pkg/types/validation.go | 3 + .../open-policy-agent/opa/ast/annotations.go | 31 +- .../open-policy-agent/opa/ast/compile.go | 204 +- .../open-policy-agent/opa/ast/env.go | 4 +- .../open-policy-agent/opa/ast/index.go | 2 +- .../opa/ast/internal/scanner/scanner.go | 17 +- .../opa/ast/location/location.go | 2 + .../open-policy-agent/opa/ast/parser.go | 30 +- .../open-policy-agent/opa/ast/parser_ext.go | 2 + .../open-policy-agent/opa/ast/policy.go | 74 +- .../open-policy-agent/opa/ast/term.go | 8 +- .../open-policy-agent/opa/ast/visit.go | 12 +- .../open-policy-agent/opa/bundle/bundle.go | 50 +- .../open-policy-agent/opa/bundle/file.go | 9 +- .../opa/capabilities/v0.65.0.json | 4826 +++++++++++++++++ .../opa/capabilities/v0.66.0.json | 4826 +++++++++++++++++ .../open-policy-agent/opa/format/format.go | 10 +- .../opa/internal/bundle/utils.go | 7 +- .../opa/internal/compiler/wasm/wasm.go | 13 +- .../opa/internal/gojsonschema/schema.go | 2 +- .../opa/internal/gojsonschema/validation.go | 8 +- .../rules/fragments_on_composite_types.go | 2 +- .../validator/rules/known_argument_names.go | 4 +- .../validator/rules/known_directives.go | 2 +- .../validator/rules/known_fragment_names.go | 2 +- .../validator/rules/no_unused_fragments.go | 4 +- .../validator/rules/no_unused_variables.go | 2 +- .../rules/provided_required_arguments.go | 4 +- .../validator/rules/unique_argument_names.go | 4 +- .../rules/unique_directives_per_location.go | 2 +- .../validator/rules/unique_fragment_names.go | 2 +- .../rules/unique_input_field_names.go | 2 +- .../validator/rules/unique_operation_names.go | 2 +- .../validator/rules/unique_variable_names.go | 2 +- .../validator/rules/values_of_correct_type.go | 2 +- .../rules/variables_are_input_types.go | 2 +- .../opa/internal/planner/planner.go | 36 +- .../opa/internal/providers/aws/signing_v4a.go | 2 +- .../opa/internal/providers/aws/util.go | 18 +- .../opa/internal/providers/aws/v4/util.go | 9 +- .../opa/internal/strings/strings.go | 8 + .../opa/internal/wasm/encoding/reader.go | 4 +- .../github.com/open-policy-agent/opa/ir/ir.go | 7 + .../open-policy-agent/opa/ir/pretty.go | 4 +- .../open-policy-agent/opa/loader/errors.go | 2 +- .../open-policy-agent/opa/loader/loader.go | 6 +- .../open-policy-agent/opa/logging/logging.go | 14 +- .../open-policy-agent/opa/plugins/plugins.go | 7 +- .../opa/plugins/rest/auth.go | 26 +- .../open-policy-agent/opa/rego/rego.go | 10 +- .../open-policy-agent/opa/topdown/bindings.go | 2 +- .../copypropagation/copypropagation.go | 6 +- .../open-policy-agent/opa/topdown/eval.go | 85 +- .../open-policy-agent/opa/topdown/http.go | 79 +- .../opa/topdown/parse_bytes.go | 2 +- .../opa/topdown/providers.go | 2 +- .../opa/topdown/reachable.go | 4 +- .../open-policy-agent/opa/topdown/semver.go | 4 +- .../open-policy-agent/opa/topdown/strings.go | 8 +- .../open-policy-agent/opa/topdown/tokens.go | 19 +- .../open-policy-agent/opa/topdown/trace.go | 469 +- .../open-policy-agent/opa/types/decode.go | 6 +- .../open-policy-agent/opa/types/types.go | 8 +- .../opa/util/read_gzip_body.go | 26 + .../open-policy-agent/opa/version/version.go | 2 +- .../image-spec/specs-go/version.go | 2 +- vendor/google.golang.org/grpc/README.md | 2 +- .../grpclb/grpc_lb_v1/load_balancer.pb.go | 2 +- .../grpc_lb_v1/load_balancer_grpc.pb.go | 2 +- .../grpc/balancer/grpclb/grpclb_config.go | 4 +- .../grpc/balancer/grpclb/grpclb_picker.go | 6 +- .../{ => balancer/pickfirst}/pickfirst.go | 32 +- .../grpc/balancer/roundrobin/roundrobin.go | 4 +- .../grpc/balancer_wrapper.go | 4 + .../grpc_binarylog_v1/binarylog.pb.go | 2 +- vendor/google.golang.org/grpc/clientconn.go | 66 +- .../internal/proto/grpc_gcp/altscontext.pb.go | 2 +- .../internal/proto/grpc_gcp/handshaker.pb.go | 2 +- .../proto/grpc_gcp/handshaker_grpc.pb.go | 2 +- .../grpc_gcp/transport_security_common.pb.go | 2 +- .../google.golang.org/grpc/credentials/tls.go | 34 +- vendor/google.golang.org/grpc/dialoptions.go | 46 + .../grpc/health/grpc_health_v1/health.pb.go | 2 +- .../health/grpc_health_v1/health_grpc.pb.go | 10 +- .../grpc/internal/backoff/backoff.go | 4 +- .../grpc/internal/envconfig/envconfig.go | 6 + .../grpc/internal/grpcrand/grpcrand.go | 100 - .../grpc/internal/grpcrand/grpcrand_go1.21.go | 73 - .../grpc/internal/internal.go | 37 +- .../internal/resolver/dns/dns_resolver.go | 14 +- .../resolver/dns/internal/internal.go | 13 +- .../grpc/internal/transport/http2_server.go | 4 +- .../google.golang.org/grpc/picker_wrapper.go | 81 +- .../grpc/resolver_wrapper.go | 2 +- .../google.golang.org/grpc/service_config.go | 24 +- vendor/google.golang.org/grpc/stream.go | 4 +- vendor/google.golang.org/grpc/version.go | 2 +- vendor/modules.txt | 25 +- 191 files changed, 13216 insertions(+), 962 deletions(-) mode change 100755 => 100644 pkg/controller/constrainttemplate/constrainttemplate_controller_test.go create mode 100644 test/bats/tests/bad/bad_cm_scoped.yaml create mode 100644 test/bats/tests/constraints/all_cm_must_have_gatekeeper_scoped.yaml create mode 100644 test/bats/tests/constraints/all_cm_must_have_gatekeeper_scoped_audit.yaml create mode 100644 test/bats/tests/constraints/all_cm_must_have_gatekeeper_scoped_webhook.yaml create mode 100644 test/bats/tests/constraints/all_ns_must_have_label_provided_vapbinding_scoped.yaml create mode 100644 test/gator/verify/constraint_with_scopedEA.yaml create mode 100644 test/gator/verify/constraint_with_scopedEA_without_gator_ep.yaml create mode 100644 vendor/github.com/containerd/containerd/errdefs/errdefs_deprecated.go rename vendor/github.com/{open-policy-agent/frameworks/constraint => containerd/errdefs}/LICENSE (93%) create mode 100644 vendor/github.com/containerd/errdefs/README.md rename vendor/github.com/containerd/{containerd => }/errdefs/errors.go (100%) rename vendor/github.com/containerd/{containerd => }/errdefs/grpc.go (100%) rename vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/{drivers/query_opts.go => reviews/review_opts.go} (51%) create mode 100644 vendor/github.com/open-policy-agent/opa/capabilities/v0.65.0.json create mode 100644 vendor/github.com/open-policy-agent/opa/capabilities/v0.66.0.json create mode 100644 vendor/github.com/open-policy-agent/opa/util/read_gzip_body.go rename vendor/google.golang.org/grpc/{ => balancer/pickfirst}/pickfirst.go (89%) delete mode 100644 vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go delete mode 100644 vendor/google.golang.org/grpc/internal/grpcrand/grpcrand_go1.21.go diff --git a/cmd/gator/test/gatortest_test.go b/cmd/gator/test/gatortest_test.go index f2e573f1293..b98d3b7753c 100644 --- a/cmd/gator/test/gatortest_test.go +++ b/cmd/gator/test/gatortest_test.go @@ -60,6 +60,7 @@ func Test_formatOutput(t *testing.T) { object: kind: kind enforcementaction: "" + scopedenforcementactions: [] violatingObject: bar: xyz trace: xyz diff --git a/cmd/gator/test/test.go b/cmd/gator/test/test.go index 315511e5964..58f911776e9 100644 --- a/cmd/gator/test/test.go +++ b/cmd/gator/test/test.go @@ -209,6 +209,11 @@ func enforceableFailure(results []*test.GatorResult) bool { if result.EnforcementAction == string(util.Deny) { return true } + for _, action := range result.ScopedEnforcementActions { + if action == string(util.Deny) { + return true + } + } } return false diff --git a/go.mod b/go.mod index 96bce7b53f8..b17ebe0d49d 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,8 @@ module github.com/open-policy-agent/gatekeeper/v3 go 1.21 +replace github.com/open-policy-agent/frameworks/constraint => /mount/d/go/src/github.com/open-policy-agent/frameworks/constraint + require ( cloud.google.com/go/trace v1.10.11 github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.44.0 @@ -34,7 +36,7 @@ require ( golang.org/x/oauth2 v0.21.0 golang.org/x/sync v0.7.0 golang.org/x/time v0.5.0 - google.golang.org/grpc v1.64.1 + google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 @@ -58,7 +60,7 @@ require ( github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.20.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.44.0 // indirect - github.com/Microsoft/hcsshim v0.11.4 // indirect + github.com/Microsoft/hcsshim v0.11.5 // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect github.com/agnivade/levenshtein v1.1.1 // indirect github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df // indirect @@ -67,9 +69,10 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect - github.com/containerd/containerd v1.7.15 // indirect + github.com/containerd/containerd v1.7.18 // indirect + github.com/containerd/errdefs v0.1.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/distribution/reference v0.5.0 // indirect github.com/docker/cli v25.0.1+incompatible // indirect @@ -112,9 +115,9 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/open-policy-agent/opa v0.64.1 // indirect + github.com/open-policy-agent/opa v0.66.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc6 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.48.0 // indirect diff --git a/go.sum b/go.sum index a8fa188ea07..b11d3f3f5fe 100644 --- a/go.sum +++ b/go.sum @@ -25,10 +25,10 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0 github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.44.0/go.mod h1:OZ0OdcedAJJyQbJsfO97KMimDYkuOkzzO4AQPgV5QRI= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.44.0 h1:GjWPDY9PUlNWwTI95L/lktUp35BLtzBoBElH314eafM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.44.0/go.mod h1:qkFPtMouQjW5ugdHIOthiTbweVHUTqbS0Qsu55KqXks= -github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= -github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= -github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/Microsoft/hcsshim v0.11.5 h1:haEcLNpj9Ka1gd3B3tAEs9CpE0c+1IhoL59w/exYU38= +github.com/Microsoft/hcsshim v0.11.5/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= @@ -66,18 +66,20 @@ github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ= github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= -github.com/containerd/containerd v1.7.15 h1:afEHXdil9iAm03BmhjzKyXnnEBtjaLJefdU7DV0IFes= -github.com/containerd/containerd v1.7.15/go.mod h1:ISzRRTMF8EXNpJlTzyr2XMhN+j9K302C21/+cr3kUnY= +github.com/containerd/containerd v1.7.18 h1:jqjZTQNfXGoEaZdW1WwPU0RqSn1Bm2Ay/KJPUuO8nao= +github.com/containerd/containerd v1.7.18/go.mod h1:IYEk9/IO6wAPUz2bCMVUbsfXjzw5UNP5fLz4PsUygQ4= github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM= +github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= @@ -287,14 +289,12 @@ github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= github.com/open-policy-agent/cert-controller v0.10.1 h1:RXSYoyn8FdCenWecRP//UV5nbVfmstNpj4kHQFkvPK4= github.com/open-policy-agent/cert-controller v0.10.1/go.mod h1:4uRbBLY5DsPOog+a9pqk3JLxuuhrWsbUedQW65HcLTI= -github.com/open-policy-agent/frameworks/constraint v0.0.0-20240703202613-6687ba5fafc8 h1:GqwFyBjq31c+QBsneTceF4yM7P3FKj4GSkIXdeXdbKU= -github.com/open-policy-agent/frameworks/constraint v0.0.0-20240703202613-6687ba5fafc8/go.mod h1:5QVgZkIf934jEqGwfBUNn0GQ9sBmOfUTF2yrKdKiZuY= -github.com/open-policy-agent/opa v0.64.1 h1:n8IJTYlFWzqiOYx+JiawbErVxiqAyXohovcZxYbskxQ= -github.com/open-policy-agent/opa v0.64.1/go.mod h1:j4VeLorVpKipnkQ2TDjWshEuV3cvP/rHzQhYaraUXZY= +github.com/open-policy-agent/opa v0.66.0 h1:DbrvfJQja0FBRcPOB3Z/BOckocN+M4ApNWyNhSRJt0w= +github.com/open-policy-agent/opa v0.66.0/go.mod h1:EIgNnJcol7AvQR/IcWLwL13k64gHVbNAVG46b2G+/EY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc6 h1:XDqvyKsJEbRtATzkgItUqBA7QHk58yxX1Ov9HERHNqU= -github.com/opencontainers/image-spec v1.1.0-rc6/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -507,8 +507,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= -google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/main.go b/main.go index a5c563ed7b6..6cedb8155a1 100644 --- a/main.go +++ b/main.go @@ -426,6 +426,16 @@ func setupControllers(ctx context.Context, mgr ctrl.Manager, sw *watch.Controlle } cfArgs = append(cfArgs, constraintclient.Driver(driver)) + eps := []string{} + if operations.IsAssigned(operations.Audit) { + eps = append(eps, util.AuditEnforcementPoint) + } + if operations.IsAssigned(operations.Webhook) { + eps = append(eps, util.WebhookEnforcementPoint) + } + + cfArgs = append(cfArgs, constraintclient.EnforcementPoints(eps...)) + client, err := constraintclient.NewClient(cfArgs...) if err != nil { setupLog.Error(err, "unable to set up OPA client") diff --git a/pkg/audit/manager.go b/pkg/audit/manager.go index 8859c429149..0128cda82cf 100644 --- a/pkg/audit/manager.go +++ b/pkg/audit/manager.go @@ -16,7 +16,7 @@ import ( "github.com/go-logr/logr" constraintclient "github.com/open-policy-agent/frameworks/constraint/pkg/client" - "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers" + "github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews" "github.com/open-policy-agent/gatekeeper/v3/pkg/controller/config/process" pubsubController "github.com/open-policy-agent/gatekeeper/v3/pkg/controller/pubsub" "github.com/open-policy-agent/gatekeeper/v3/pkg/expansion" @@ -96,13 +96,14 @@ type Manager struct { // StatusViolation represents each violation under status. type StatusViolation struct { - Group string `json:"group"` - Version string `json:"version"` - Kind string `json:"kind"` - Name string `json:"name"` - Namespace string `json:"namespace,omitempty"` - Message string `json:"message"` - EnforcementAction string `json:"enforcementAction"` + Group string `json:"group"` + Version string `json:"version"` + Kind string `json:"kind"` + Name string `json:"name"` + Namespace string `json:"namespace,omitempty"` + Message string `json:"message"` + EnforcementAction string `json:"enforcementAction"` + EnforcementActions []string `json:"enforcementActions,omitempty"` } // ConstraintMsg represents publish message for each constraint. @@ -117,6 +118,7 @@ type PubsubMsg struct { Namespace string `json:"namespace,omitempty"` Message string `json:"message,omitempty"` EnforcementAction string `json:"enforcementAction,omitempty"` + EnforcementActions []string `json:"enforcementActions,omitempty"` ConstraintAnnotations map[string]string `json:"constraintAnnotations,omitempty"` ResourceGroup string `json:"resourceGroup,omitempty"` ResourceAPIVersion string `json:"resourceAPIVersion,omitempty"` @@ -607,7 +609,7 @@ func (am *Manager) auditFromCache(ctx context.Context) ([]Result, []error) { Object: obj, Namespace: ns, } - resp, err := am.opa.Review(ctx, au, drivers.Stats(*logStatsAudit)) + resp, err := am.opa.Review(ctx, au, reviews.SourceEP(util.AuditEnforcementPoint), reviews.Stats(*logStatsAudit), reviews.SourceEP(util.AuditEnforcementPoint)) if err != nil { am.log.Error(err, fmt.Sprintf("Unable to review object from audit cache %v %s/%s", obj.GroupVersionKind().String(), obj.GetNamespace(), obj.GetName())) continue @@ -697,7 +699,7 @@ func (am *Manager) reviewObjects(ctx context.Context, kind string, folderCount i Source: mutationtypes.SourceTypeOriginal, } - resp, err := am.opa.Review(ctx, augmentedObj, drivers.Stats(*logStatsAudit)) + resp, err := am.opa.Review(ctx, augmentedObj, reviews.SourceEP(util.AuditEnforcementPoint), reviews.Stats(*logStatsAudit), reviews.SourceEP(util.AuditEnforcementPoint)) if err != nil { am.log.Error(err, "Unable to review object from file", "fileName", fileName, "objNs", objNs) continue @@ -721,7 +723,7 @@ func (am *Manager) reviewObjects(ctx context.Context, kind string, folderCount i Namespace: ns, Source: mutationtypes.SourceTypeGenerated, } - resultantResp, err := am.opa.Review(ctx, au, drivers.Stats(*logStatsAudit)) + resultantResp, err := am.opa.Review(ctx, au, reviews.SourceEP(util.AuditEnforcementPoint), reviews.Stats(*logStatsAudit), reviews.SourceEP(util.AuditEnforcementPoint)) if err != nil { am.log.Error(err, "Unable to review expanded object", "objName", (*resultant.Obj).GetName(), "objNs", ns) continue @@ -883,15 +885,15 @@ func (am *Manager) addAuditResponsesToUpdateLists( if len(msg) > msgSize { msg = truncateString(msg, msgSize) } - action := string(ea) violation := &StatusViolation{ - Group: gvk.Group, - Version: gvk.Version, - Kind: gvk.Kind, - Namespace: namespace, - Name: name, - Message: msg, - EnforcementAction: action, + Group: gvk.Group, + Version: gvk.Version, + Kind: gvk.Kind, + Namespace: namespace, + Name: name, + Message: msg, + EnforcementAction: r.EnforcementAction, + EnforcementActions: r.ScopedEnforcementActions, } // since keyQueue is a LimitQueue, it guarantees len <= limit after a push. // the limit on size ensures Push() has O(1) time complexity. @@ -899,9 +901,9 @@ func (am *Manager) addAuditResponsesToUpdateLists( details := r.Metadata["details"] labels := r.obj.GetLabels() - logViolation(am.log, constraint, ea, gvk, namespace, name, msg, details, labels) + logViolation(am.log, constraint, ea, r.ScopedEnforcementActions, gvk, namespace, name, msg, details, labels) if *pubsubController.PubsubEnabled { - err := am.pubsubSystem.Publish(context.Background(), *auditConnection, *auditChannel, violationMsg(constraint, ea, gvk, namespace, name, msg, details, labels, timestamp)) + err := am.pubsubSystem.Publish(context.Background(), *auditConnection, *auditChannel, violationMsg(constraint, ea, r.ScopedEnforcementActions, gvk, namespace, name, msg, details, labels, timestamp)) if err != nil { am.log.Error(err, "pubsub audit Publishing") } @@ -909,7 +911,7 @@ func (am *Manager) addAuditResponsesToUpdateLists( if *emitAuditEvents { uid := r.obj.GetUID() rv := r.obj.GetResourceVersion() - emitEvent(constraint, timestamp, ea, gvk, namespace, name, rv, msg, am.gkNamespace, uid, am.eventRecorder) + emitEvent(constraint, timestamp, ea, strings.Join(r.ScopedEnforcementActions, ","), gvk, namespace, name, rv, msg, am.gkNamespace, uid, am.eventRecorder) } } } @@ -1155,7 +1157,7 @@ func logConstraint(l logr.Logger, gvknn *util.KindVersionName, enforcementAction ) } -func violationMsg(constraint *unstructured.Unstructured, enforcementAction util.EnforcementAction, resourceGroupVersionKind schema.GroupVersionKind, rnamespace, rname, message string, details interface{}, rlabels map[string]string, timestamp string) interface{} { +func violationMsg(constraint *unstructured.Unstructured, enforcementAction util.EnforcementAction, scopedEnforcementActions []string, resourceGroupVersionKind schema.GroupVersionKind, rnamespace, rname, message string, details interface{}, rlabels map[string]string, timestamp string) interface{} { userConstraintAnnotations := constraint.GetAnnotations() delete(userConstraintAnnotations, "kubectl.kubernetes.io/last-applied-configuration") @@ -1170,6 +1172,7 @@ func violationMsg(constraint *unstructured.Unstructured, enforcementAction util. Name: constraint.GetName(), Namespace: constraint.GetNamespace(), EnforcementAction: string(enforcementAction), + EnforcementActions: scopedEnforcementActions, ConstraintAnnotations: userConstraintAnnotations, ResourceGroup: resourceGroupVersionKind.Group, ResourceAPIVersion: resourceGroupVersionKind.Version, @@ -1182,7 +1185,7 @@ func violationMsg(constraint *unstructured.Unstructured, enforcementAction util. func logViolation(l logr.Logger, constraint *unstructured.Unstructured, - enforcementAction util.EnforcementAction, resourceGroupVersionKind schema.GroupVersionKind, rnamespace, rname, message string, details interface{}, rlabels map[string]string, + enforcementAction util.EnforcementAction, scopedEnforcementActions []string, resourceGroupVersionKind schema.GroupVersionKind, rnamespace, rname, message string, details interface{}, rlabels map[string]string, ) { userConstraintAnnotations := constraint.GetAnnotations() delete(userConstraintAnnotations, "kubectl.kubernetes.io/last-applied-configuration") @@ -1197,6 +1200,7 @@ func logViolation(l logr.Logger, logging.ConstraintName, constraint.GetName(), logging.ConstraintNamespace, constraint.GetNamespace(), logging.ConstraintAction, enforcementAction, + logging.ConstraintEnforcementActions, scopedEnforcementActions, logging.ConstraintAnnotations, userConstraintAnnotations, logging.ResourceGroup, resourceGroupVersionKind.Group, logging.ResourceAPIVersion, resourceGroupVersionKind.Version, @@ -1208,24 +1212,25 @@ func logViolation(l logr.Logger, } func emitEvent(constraint *unstructured.Unstructured, - timestamp string, enforcementAction util.EnforcementAction, resourceGroupVersionKind schema.GroupVersionKind, rnamespace, rname, rrv, message, gkNamespace string, ruid types.UID, + timestamp string, enforcementAction util.EnforcementAction, scopedEnforcementActions string, resourceGroupVersionKind schema.GroupVersionKind, rnamespace, rname, rrv, message, gkNamespace string, ruid types.UID, eventRecorder record.EventRecorder, ) { annotations := map[string]string{ - "process": "audit", - "auditTimestamp": timestamp, - logging.EventType: "violation_audited", - logging.ConstraintGroup: constraint.GroupVersionKind().Group, - logging.ConstraintAPIVersion: constraint.GroupVersionKind().Version, - logging.ConstraintKind: constraint.GetKind(), - logging.ConstraintName: constraint.GetName(), - logging.ConstraintNamespace: constraint.GetNamespace(), - logging.ConstraintAction: string(enforcementAction), - logging.ResourceGroup: resourceGroupVersionKind.Group, - logging.ResourceAPIVersion: resourceGroupVersionKind.Version, - logging.ResourceKind: resourceGroupVersionKind.Kind, - logging.ResourceNamespace: rnamespace, - logging.ResourceName: rname, + "process": "audit", + "auditTimestamp": timestamp, + logging.EventType: "violation_audited", + logging.ConstraintGroup: constraint.GroupVersionKind().Group, + logging.ConstraintAPIVersion: constraint.GroupVersionKind().Version, + logging.ConstraintKind: constraint.GetKind(), + logging.ConstraintName: constraint.GetName(), + logging.ConstraintNamespace: constraint.GetNamespace(), + logging.ConstraintAction: string(enforcementAction), + logging.ConstraintEnforcementActions: scopedEnforcementActions, + logging.ResourceGroup: resourceGroupVersionKind.Group, + logging.ResourceAPIVersion: resourceGroupVersionKind.Version, + logging.ResourceKind: resourceGroupVersionKind.Kind, + logging.ResourceNamespace: rnamespace, + logging.ResourceName: rname, } reason := "AuditViolation" diff --git a/pkg/audit/manager_test.go b/pkg/audit/manager_test.go index b74d534d5fd..b934b162bd2 100644 --- a/pkg/audit/manager_test.go +++ b/pkg/audit/manager_test.go @@ -13,6 +13,7 @@ import ( "github.com/open-policy-agent/gatekeeper/v3/pkg/controller/config/process" "github.com/open-policy-agent/gatekeeper/v3/pkg/fakes" "github.com/open-policy-agent/gatekeeper/v3/pkg/target" + "github.com/open-policy-agent/gatekeeper/v3/pkg/util" "github.com/open-policy-agent/gatekeeper/v3/pkg/wildcard" "github.com/pkg/errors" "github.com/stretchr/testify/require" @@ -96,32 +97,47 @@ func Test_auditFromCache(t *testing.T) { driver, err := rego.New() require.NoError(t, err) - client, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver)) + client, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints([]string{util.AuditEnforcementPoint}...)) require.NoError(t, err) _, err = client.AddTemplate(context.Background(), fakes.DenyAllRegoTemplate()) require.NoError(t, err, "adding denyall constraint template") - _, err = client.AddConstraint(context.Background(), fakes.DenyAllConstraint()) - require.NoError(t, err, "adding denyall constraint") tests := []struct { name string processExcluder *process.Excluder + constraint *unstructured.Unstructured wantViolation bool }{ { name: "obj excluded from audit", processExcluder: processExcluderFor([]string{"test-namespace-1"}), + constraint: fakes.DenyAllConstraint(), }, { name: "obj not excluded from audit", processExcluder: processExcluderFor([]string{}), + constraint: fakes.DenyAllConstraint(), + wantViolation: true, + }, + { + name: "audit excluded from constraint", + processExcluder: processExcluderFor([]string{}), + constraint: fakes.WebhookDenyAllConstraint(), + }, + { + name: "audit included in constraints", + processExcluder: processExcluderFor([]string{}), + constraint: fakes.AuditDenyAllConstraint(), wantViolation: true, }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { + _, err = client.AddConstraint(context.Background(), tc.constraint) + require.NoError(t, err, "adding denyall constraint") + am := &Manager{ processExcluder: tc.processExcluder, auditCache: testAuditCache, @@ -136,6 +152,10 @@ func Test_auditFromCache(t *testing.T) { } else { require.Len(t, results, 0) } + + if _, err := client.RemoveConstraint(context.Background(), tc.constraint); err != nil { + t.Fatal(err) + } }) } } diff --git a/pkg/controller/constraint/constraint_controller.go b/pkg/controller/constraint/constraint_controller.go index 7fa2010728d..bb5627852ad 100755 --- a/pkg/controller/constraint/constraint_controller.go +++ b/pkg/controller/constraint/constraint_controller.go @@ -288,9 +288,6 @@ func (r *ReconcileConstraint) Reconcile(ctx context.Context, request reconcile.R return reconcile.Result{}, err } - generateVAPB := *DefaultGenerateVAPB && HasVAPCel(ct) - r.log.Info("constraint controller", "generateVAPB", generateVAPB) - if !deleted { r.log.Info("handling constraint update", "instance", instance) status, err := r.getOrCreatePodStatus(ctx, instance) @@ -303,8 +300,27 @@ func (r *ReconcileConstraint) Reconcile(ctx context.Context, request reconcile.R status.Status.Errors = nil if c, err := r.cfClient.GetConstraint(instance); err != nil || !reflect.DeepEqual(instance, c) { + generateVAPB, VAPEnforcementActions, err := shouldGenerateVAPB(*DefaultGenerateVAPB, enforcementAction, instance) + if err != nil { + status.Status.Errors = append(status.Status.Errors, constraintstatusv1beta1.Error{Message: err.Error()}) + log.Error(err, "could not get enforcement actions for VAP") + if err2 := r.writer.Update(ctx, status); err2 != nil { + log.Error(err2, "could not report error for getting enforcement actions for VAP") + } + return reconcile.Result{}, err + } + isVAPBGenerationEnabled := generateVAPB && IsVapAPIEnabled() && HasVAPCel(ct) + if generateVAPB != isVAPBGenerationEnabled { + if !IsVapAPIEnabled() { + r.log.V(1).Info("Warning: VAP API is not enabled, cannot create VAPBinding") + } + if !HasVAPCel(ct) { + r.log.V(1).Info("Warning: ConstraintTemplate does not contain VAP-style CEL source, cannot create VAPBinding") + } + } + r.log.Info("constraint controller", "generateVAPB", isVAPBGenerationEnabled) // generate vapbinding resources - if generateVAPB && IsVapAPIEnabled() { + if isVAPBGenerationEnabled { currentVapBinding := &admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding{} vapBindingName := fmt.Sprintf("gatekeeper-%s", instance.GetName()) log.Info("check if vapbinding exists", "vapBindingName", vapBindingName) @@ -315,7 +331,7 @@ func (r *ReconcileConstraint) Reconcile(ctx context.Context, request reconcile.R currentVapBinding = nil } newVapBinding := &admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding{} - transformedVapBinding, err := transform.ConstraintToBinding(instance) + transformedVapBinding, err := transform.ConstraintToBinding(instance, VAPEnforcementActions) if err != nil { status.Status.Errors = append(status.Status.Errors, constraintstatusv1beta1.Error{Message: err.Error()}) if err2 := r.writer.Update(ctx, status); err2 != nil { @@ -356,7 +372,7 @@ func (r *ReconcileConstraint) Reconcile(ctx context.Context, request reconcile.R } // do not generate vapbinding resources // remove if exists - if !generateVAPB && IsVapAPIEnabled() { + if (!generateVAPB || !HasVAPCel(ct)) && IsVapAPIEnabled() { currentVapBinding := &admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding{} vapBindingName := fmt.Sprintf("gatekeeper-%s", instance.GetName()) log.Info("check if vapbinding exists", "vapBindingName", vapBindingName) @@ -435,6 +451,20 @@ func (r *ReconcileConstraint) Reconcile(ctx context.Context, request reconcile.R return reconcile.Result{}, nil } +func shouldGenerateVAPB(defaultGenerateVAPB bool, enforcementAction util.EnforcementAction, instance *unstructured.Unstructured) (bool, []string, error) { + var VAPEnforcementActions []string + var err error + switch enforcementAction { + case util.Scoped: + VAPEnforcementActions, err = util.ScopedActionForEP(util.VAPEnforcementPoint, instance) + default: + if defaultGenerateVAPB { + VAPEnforcementActions = []string{string(enforcementAction)} + } + } + return len(VAPEnforcementActions) != 0, VAPEnforcementActions, err +} + func (r *ReconcileConstraint) defaultGetPod(_ context.Context) (*corev1.Pod, error) { // require injection of GetPod in order to control what client we use to // guarantee we don't inadvertently create a watch diff --git a/pkg/controller/constraint/constraint_controller_test.go b/pkg/controller/constraint/constraint_controller_test.go index f6d0a3c9810..d1404209f12 100644 --- a/pkg/controller/constraint/constraint_controller_test.go +++ b/pkg/controller/constraint/constraint_controller_test.go @@ -1,12 +1,16 @@ package constraint import ( + "errors" + "reflect" "testing" "github.com/davecgh/go-spew/spew" + apiconstraints "github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints" "github.com/open-policy-agent/frameworks/constraint/pkg/apis/templates/v1beta1" "github.com/open-policy-agent/gatekeeper/v3/pkg/metrics" "github.com/open-policy-agent/gatekeeper/v3/pkg/util" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) func TestTotalConstraintsCache(t *testing.T) { @@ -91,3 +95,250 @@ func TestHasVAPCel(t *testing.T) { t.Errorf("hasVAPCel() = %v, expected %v", result, expected) } } + +func TestShouldGenerateVAPB(t *testing.T) { + testCases := []struct { + name string + enforcementAction util.EnforcementAction + defGenerateVAPB bool + instance *unstructured.Unstructured + expectedGenerate bool + expectedError error + expectedVAPEnforcementActions []string + }{ + { + name: "defaultGenerateVAPB is false, enforcementAction is Deny", + enforcementAction: util.Deny, + defGenerateVAPB: false, + instance: &unstructured.Unstructured{}, + expectedGenerate: false, + }, + { + name: "defaultGenerateVAPB is true, enforcementAction is Dryrun", + enforcementAction: util.Dryrun, + defGenerateVAPB: true, + instance: &unstructured.Unstructured{}, + expectedGenerate: true, + expectedVAPEnforcementActions: []string{"dryrun"}, + }, + { + name: "defaultGenerateVAPB is false, enforcementAction is Scoped, VAP ep is not set", + enforcementAction: util.Scoped, + defGenerateVAPB: false, + instance: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "spec": map[string]interface{}{ + "enforcementAction": "scoped", + "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ + { + Action: "deny", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "webhook", + }, + }, + }, + { + Action: "warn", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "webhook", + }, + }, + }, + }, + }, + }, + }, + expectedGenerate: false, + expectedVAPEnforcementActions: []string{}, + }, + { + name: "defaultGenerateVAPB is true, enforcementAction is Scoped, VAP ep is not set", + enforcementAction: util.Scoped, + defGenerateVAPB: true, + instance: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "spec": map[string]interface{}{ + "enforcementAction": "scoped", + "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ + { + Action: "deny", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "audit", + }, + }, + }, + { + Action: "warn", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "audit", + }, + }, + }, + }, + }, + }, + }, + expectedGenerate: false, + expectedVAPEnforcementActions: []string{}, + }, + { + name: "defaultGenerateVAPB is false, enforcementAction is Scoped, VAP ep is set", + enforcementAction: util.Scoped, + defGenerateVAPB: false, + instance: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "spec": map[string]interface{}{ + "enforcementAction": "scoped", + "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ + { + Action: "deny", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "webhook", + }, + { + Name: util.VAPEnforcementPoint, + }, + }, + }, + { + Action: "warn", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "webhook", + }, + }, + }, + }, + }, + }, + }, + expectedGenerate: true, + expectedVAPEnforcementActions: []string{"deny"}, + }, + { + name: "defaultGenerateVAPB is true, enforcementAction is Scoped, VAP ep is set", + enforcementAction: util.Scoped, + defGenerateVAPB: true, + instance: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "spec": map[string]interface{}{ + "enforcementAction": "scoped", + "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ + { + Action: "deny", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "audit", + }, + { + Name: util.VAPEnforcementPoint, + }, + }, + }, + { + Action: "warn", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "audit", + }, + { + Name: util.VAPEnforcementPoint, + }, + }, + }, + }, + }, + }, + }, + expectedGenerate: true, + expectedVAPEnforcementActions: []string{"deny", "warn"}, + }, + { + name: "defaultGenerateVAPB is true, enforcementAction is Scoped, wildcard ep is set", + enforcementAction: util.Scoped, + defGenerateVAPB: true, + instance: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "spec": map[string]interface{}{ + "enforcementAction": "scoped", + "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ + { + Action: "deny", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "*", + }, + }, + }, + { + Action: "warn", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "audit", + }, + }, + }, + }, + }, + }, + }, + expectedGenerate: true, + expectedVAPEnforcementActions: []string{"deny"}, + }, + { + name: "defaultGenerateVAPB is false, enforcementAction is Scoped, wildcard ep is set", + enforcementAction: util.Scoped, + defGenerateVAPB: false, + instance: &unstructured.Unstructured{ + Object: map[string]interface{}{ + "spec": map[string]interface{}{ + "enforcementAction": "scoped", + "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ + { + Action: "deny", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "*", + }, + }, + }, + { + Action: "warn", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "audit", + }, + }, + }, + }, + }, + }, + }, + expectedGenerate: true, + expectedVAPEnforcementActions: []string{"deny"}, + }, + } + + for _, tc := range testCases { + if tc.name == "" { + tc.name = string(tc.enforcementAction) + } + t.Run(tc.name, func(t *testing.T) { + generate, VAPEnforcementActions, err := shouldGenerateVAPB(tc.defGenerateVAPB, tc.enforcementAction, tc.instance) + if err != nil && (err.Error() != errors.New("scopedEnforcementActions is required").Error()) { + t.Errorf("shouldGenerateVAPB returned an unexpected error: %v", err) + } + if generate != tc.expectedGenerate { + t.Errorf("shouldGenerateVAPB returned generate = %v, expected %v", generate, tc.expectedGenerate) + } + if !reflect.DeepEqual(VAPEnforcementActions, tc.expectedVAPEnforcementActions) { + t.Errorf("shouldGenerateVAPB returned VAPEnforcementActions = %v, expected %v", VAPEnforcementActions, tc.expectedVAPEnforcementActions) + } + }) + } +} diff --git a/pkg/controller/constraint/stats_reporter_test.go b/pkg/controller/constraint/stats_reporter_test.go index 4f341b06e62..6e15a2079d0 100644 --- a/pkg/controller/constraint/stats_reporter_test.go +++ b/pkg/controller/constraint/stats_reporter_test.go @@ -50,6 +50,8 @@ func TestReportConstraints(t *testing.T) { {Attributes: attribute.NewSet(attribute.String(enforcementActionKey, string(util.Warn)), attribute.String(statusKey, string(metrics.ErrorStatus))), Value: 0}, {Attributes: attribute.NewSet(attribute.String(enforcementActionKey, string(util.Deny)), attribute.String(statusKey, string(metrics.ActiveStatus))), Value: 0}, {Attributes: attribute.NewSet(attribute.String(enforcementActionKey, string(util.Dryrun)), attribute.String(statusKey, string(metrics.ActiveStatus))), Value: 0}, + {Attributes: attribute.NewSet(attribute.String(enforcementActionKey, string(util.Scoped)), attribute.String(statusKey, string(metrics.ActiveStatus))), Value: 0}, + {Attributes: attribute.NewSet(attribute.String(enforcementActionKey, string(util.Scoped)), attribute.String(statusKey, string(metrics.ErrorStatus))), Value: 0}, {Attributes: attribute.NewSet(attribute.String(enforcementActionKey, string(util.Unrecognized)), attribute.String(statusKey, string(metrics.ActiveStatus))), Value: 0}, {Attributes: attribute.NewSet(attribute.String(enforcementActionKey, string(util.Unrecognized)), attribute.String(statusKey, string(metrics.ErrorStatus))), Value: 0}, }, diff --git a/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go b/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go old mode 100755 new mode 100644 index 28f4b7502f2..1edbe2deb40 --- a/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go +++ b/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go @@ -28,6 +28,7 @@ import ( "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/k8scel" celSchema "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/k8scel/schema" "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/rego" + "github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews" "github.com/open-policy-agent/frameworks/constraint/pkg/core/templates" statusv1beta1 "github.com/open-policy-agent/gatekeeper/v3/apis/status/v1beta1" "github.com/open-policy-agent/gatekeeper/v3/pkg/controller/constraint" @@ -235,7 +236,7 @@ func TestReconcile(t *testing.T) { t.Fatalf("unable to set up K8s native driver: %v", err) } - cfClient, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.Driver(k8sDriver)) + cfClient, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.Driver(k8sDriver), constraintclient.EnforcementPoints(util.AuditEnforcementPoint)) if err != nil { t.Fatalf("unable to set up constraint framework client: %s", err) } @@ -436,7 +437,6 @@ func TestReconcile(t *testing.T) { t.Run("Vap should be created without generateVAP field", func(t *testing.T) { suffix := "VapShouldBeCreatedWithoutGenerateVAP" - // TODO: set generate VAP flag to true logger.Info("Running test: Vap should be created") defaultGenerateVAP = ptr.To[bool](true) constraintTemplate := makeReconcileConstraintTemplateForVap(suffix, nil) @@ -489,8 +489,8 @@ func TestReconcile(t *testing.T) { } }) - t.Run("VapBinding should not be created", func(t *testing.T) { - suffix := "VapBindingShouldNotBeCreated" + t.Run("VapBinding should not be created with missing CEL", func(t *testing.T) { + suffix := "VapBindingShouldNotBeCreatedMissingCEL" logger.Info("Running test: VapBinding should not be created") constraint.DefaultGenerateVAPB = ptr.To[bool](true) constraintTemplate := makeReconcileConstraintTemplate(suffix) @@ -553,6 +553,70 @@ func TestReconcile(t *testing.T) { } }) + t.Run("VapBinding should be created with VAP enforcement Point", func(t *testing.T) { + suffix := "VapBindingShouldBeCreatedWithVAPEnforcementPoint" + logger.Info("Running test: VapBinding should be created with VAP enforcement point") + constraint.DefaultGenerateVAPB = ptr.To[bool](false) + constraintTemplate := makeReconcileConstraintTemplateForVap(suffix, ptr.To[bool](true)) + cstr := newDenyAllCstrWithScopedEA(suffix, util.VAPEnforcementPoint) + t.Cleanup(testutils.DeleteObjectAndConfirm(ctx, t, c, expectedCRD(suffix))) + testutils.CreateThenCleanup(ctx, t, c, constraintTemplate) + + err = retry.OnError(testutils.ConstantRetry, func(_ error) bool { + return true + }, func() error { + return c.Create(ctx, cstr) + }) + if err != nil { + logger.Error(err, "create cstr") + t.Fatal(err) + } + err = retry.OnError(testutils.ConstantRetry, func(_ error) bool { + return true + }, func() error { + // check if vapbinding resource exists now + vapBinding := &admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding{} + vapBindingName := "gatekeeper-denyallconstraint" + if err := c.Get(ctx, types.NamespacedName{Name: vapBindingName}, vapBinding); err != nil { + return err + } + return nil + }) + if err != nil { + t.Fatal(err) + } + }) + + t.Run("VapBinding should not be created without VAP enforcement Point", func(t *testing.T) { + suffix := "VapBShouldNotBeCreatedWithoutVAPEP" + logger.Info("Running test: VapBinding should be created with VAP enforcement point") + constraint.DefaultGenerateVAPB = ptr.To[bool](true) + constraintTemplate := makeReconcileConstraintTemplateForVap(suffix, ptr.To[bool](true)) + cstr := newDenyAllCstrWithScopedEA(suffix, util.AuditEnforcementPoint) + t.Cleanup(testutils.DeleteObjectAndConfirm(ctx, t, c, expectedCRD(suffix))) + testutils.CreateThenCleanup(ctx, t, c, constraintTemplate) + + err = retry.OnError(testutils.ConstantRetry, func(_ error) bool { + return true + }, func() error { + return c.Create(ctx, cstr) + }) + if err != nil { + logger.Error(err, "create cstr") + t.Fatal(err) + } + // check if vapbinding resource exists now + vapBinding := &admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding{} + vapBindingName := fmt.Sprintf("gatekeeper-%s", denyall+strings.ToLower(suffix)) + if err := c.Get(ctx, types.NamespacedName{Name: vapBindingName}, vapBinding); err != nil { + if !apierrors.IsNotFound(err) { + t.Fatal(err) + } + } else { + t.Fatal("should result in error, vapbinding not found") + } + }) + t.Run("Constraint is marked as enforced", func(t *testing.T) { suffix := "MarkedEnforced" @@ -593,7 +657,7 @@ func TestReconcile(t *testing.T) { Name: "FooNamespace", Object: runtime.RawExtension{Object: ns}, } - resp, err := cfClient.Review(ctx, req) + resp, err := cfClient.Review(ctx, req, reviews.SourceEP(util.AuditEnforcementPoint)) if err != nil { t.Fatal(err) } @@ -787,7 +851,7 @@ func TestReconcile(t *testing.T) { Name: "FooNamespace", Object: runtime.RawExtension{Object: ns}, } - resp, err := cfClient.Review(ctx, req) + resp, err := cfClient.Review(ctx, req, reviews.SourceEP(util.AuditEnforcementPoint)) if err != nil { t.Fatal(err) } @@ -808,7 +872,7 @@ func TestReconcile(t *testing.T) { err = retry.OnError(testutils.ConstantRetry, func(_ error) bool { return true }, func() error { - resp, err := cfClient.Review(ctx, req) + resp, err := cfClient.Review(ctx, req, reviews.SourceEP(util.AuditEnforcementPoint)) if err != nil { return err } @@ -1104,6 +1168,18 @@ func TestShouldGenerateVAP(t *testing.T) { expected: false, wantErr: false, }, + { + name: "missing, default 'yes'", + template: makeTemplate(nil), + vapDefault: true, + expected: true, + }, + { + name: "missing, default 'no'", + template: makeTemplate(nil), + vapDefault: false, + expected: false, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { @@ -1151,6 +1227,32 @@ func newDenyAllCstr(suffix string) *unstructured.Unstructured { return cstr } +func newDenyAllCstrWithScopedEA(suffix string, ep string) *unstructured.Unstructured { + cstr := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "spec": map[string]interface{}{ + "scopedEnforcementActions": []interface{}{ + map[string]interface{}{ + "enforcementPoints": []interface{}{ + map[string]interface{}{ + "name": ep, + }, + }, + "action": "deny", + }, + }, + }, + }, + } + cstr.SetGroupVersionKind(schema.GroupVersionKind{ + Group: "constraints.gatekeeper.sh", + Version: "v1beta1", + Kind: DenyAll + suffix, + }) + cstr.SetName("denyallconstraint") + return cstr +} + func getCTByPodStatus(templ *v1beta1.ConstraintTemplate) (v1beta1.ByPodStatus, bool) { statuses := templ.Status.ByPod for _, s := range statuses { diff --git a/pkg/fakes/fixtures.go b/pkg/fakes/fixtures.go index 29f5cff9ed1..ac87e9db05e 100644 --- a/pkg/fakes/fixtures.go +++ b/pkg/fakes/fixtures.go @@ -5,6 +5,7 @@ import ( templatesv1beta1 "github.com/open-policy-agent/frameworks/constraint/pkg/apis/templates/v1beta1" "github.com/open-policy-agent/frameworks/constraint/pkg/core/templates" "github.com/open-policy-agent/gatekeeper/v3/pkg/target" + "github.com/open-policy-agent/gatekeeper/v3/pkg/util" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" @@ -49,6 +50,51 @@ func DenyAllConstraint() *unstructured.Unstructured { return ConstraintFor("denyall") } +func AuditDenyAllConstraint() *unstructured.Unstructured { + return ScopedConstraintFor(util.AuditEnforcementPoint) +} + +func WebhookDenyAllConstraint() *unstructured.Unstructured { + return ScopedConstraintFor(util.WebhookEnforcementPoint) +} + +func ScopedConstraintFor(ep string) *unstructured.Unstructured { + u := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "spec": map[string]interface{}{ + "enforcementAction": "scoped", + "scopedEnforcementActions": []interface{}{ + map[string]interface{}{ + "enforcementPoints": []interface{}{ + map[string]interface{}{ + "name": ep, + }, + }, + "action": "deny", + }, + map[string]interface{}{ + "enforcementPoints": []interface{}{ + map[string]interface{}{ + "name": ep, + }, + }, + "action": "warn", + }, + }, + }, + }, + } + + u.SetGroupVersionKind(schema.GroupVersionKind{ + Group: constraints.Group, + Version: "v1beta1", + Kind: "denyall", + }) + u.SetName("constraint") + + return u +} + func ConstraintFor(kind string) *unstructured.Unstructured { u := &unstructured.Unstructured{} diff --git a/pkg/gator/client.go b/pkg/gator/client.go index f1db472b3af..fd795c26cbd 100644 --- a/pkg/gator/client.go +++ b/pkg/gator/client.go @@ -3,7 +3,7 @@ package gator import ( "context" - "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers" + "github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews" "github.com/open-policy-agent/frameworks/constraint/pkg/core/templates" "github.com/open-policy-agent/frameworks/constraint/pkg/types" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -29,5 +29,5 @@ type Client interface { RemoveData(ctx context.Context, data interface{}) (*types.Responses, error) // Review runs all Constraints against obj. - Review(ctx context.Context, obj interface{}, opts ...drivers.QueryOpt) (*types.Responses, error) + Review(ctx context.Context, obj interface{}, opts ...reviews.ReviewOpt) (*types.Responses, error) } diff --git a/pkg/gator/fixtures/fixtures.go b/pkg/gator/fixtures/fixtures.go index 9a4a0b7e4d0..1b4e6e944e2 100644 --- a/pkg/gator/fixtures/fixtures.go +++ b/pkg/gator/fixtures/fixtures.go @@ -214,6 +214,35 @@ metadata: name: always-fail ` + ConstraintGatorValidate = ` +kind: NeverValidate +apiVersion: constraints.gatekeeper.sh/v1beta1 +metadata: + name: always-fail-gator +spec: + enforcementAction: scoped + scopedEnforcementActions: + - enforcementPoints: + - name: gator.gatekeeper.sh + action: deny + - enforcementPoints: + - name: gator.gatekeeper.sh + action: warn +` + + ConstraintAuditValidate = ` +kind: NeverValidate +apiVersion: constraints.gatekeeper.sh/v1beta1 +metadata: + name: always-pass-gator +spec: + enforcementAction: scoped + scopedEnforcementActions: + - enforcementPoints: + - name: audit.gatekeeper.sh + action: deny +` + ConstraintNeverValidateTwice = ` kind: NeverValidateTwice apiVersion: constraints.gatekeeper.sh/v1beta1 diff --git a/pkg/gator/opa.go b/pkg/gator/opa.go index 3871e131ce7..925bb46c550 100644 --- a/pkg/gator/opa.go +++ b/pkg/gator/opa.go @@ -5,6 +5,7 @@ import ( "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/k8scel" "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/rego" "github.com/open-policy-agent/gatekeeper/v3/pkg/target" + "github.com/open-policy-agent/gatekeeper/v3/pkg/util" ) func NewOPAClient(includeTrace bool, k8sCEL bool) (Client, error) { @@ -23,7 +24,7 @@ func NewOPAClient(includeTrace bool, k8sCEL bool) (Client, error) { return nil, err } - args = append(args, constraintclient.Driver(driver)) + args = append(args, constraintclient.Driver(driver), constraintclient.EnforcementPoints([]string{util.GatorEnforcementPoint}...)) c, err := constraintclient.NewClient(args...) if err != nil { diff --git a/pkg/gator/test/test.go b/pkg/gator/test/test.go index d09aafcb616..f5c97f56475 100644 --- a/pkg/gator/test/test.go +++ b/pkg/gator/test/test.go @@ -9,11 +9,13 @@ import ( constraintclient "github.com/open-policy-agent/frameworks/constraint/pkg/client" "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/k8scel" "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/rego" + "github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews" "github.com/open-policy-agent/gatekeeper/v3/pkg/expansion" "github.com/open-policy-agent/gatekeeper/v3/pkg/gator/expand" "github.com/open-policy-agent/gatekeeper/v3/pkg/gator/reader" mutationtypes "github.com/open-policy-agent/gatekeeper/v3/pkg/mutation/types" "github.com/open-policy-agent/gatekeeper/v3/pkg/target" + "github.com/open-policy-agent/gatekeeper/v3/pkg/util" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" ) @@ -48,7 +50,7 @@ func Test(objs []*unstructured.Unstructured, tOpts Opts) (*GatorResponses, error if err != nil { return nil, fmt.Errorf("creating Rego driver: %w", err) } - args = append(args, constraintclient.Driver(driver)) + args = append(args, constraintclient.Driver(driver), constraintclient.EnforcementPoints(util.GatorEnforcementPoint)) client, err := constraintclient.NewClient(args...) if err != nil { @@ -113,7 +115,7 @@ func Test(objs []*unstructured.Unstructured, tOpts Opts) (*GatorResponses, error Source: mutationtypes.SourceTypeOriginal, } - review, err := client.Review(ctx, au) + review, err := client.Review(ctx, au, reviews.SourceEP(util.GatorEnforcementPoint)) if err != nil { return nil, fmt.Errorf("reviewing %v %s/%s: %w", obj.GroupVersionKind(), obj.GetNamespace(), obj.GetName(), err) @@ -130,7 +132,7 @@ func Test(objs []*unstructured.Unstructured, tOpts Opts) (*GatorResponses, error Namespace: ns, Source: mutationtypes.SourceTypeGenerated, } - resultantReview, err := client.Review(ctx, au) + resultantReview, err := client.Review(ctx, au, reviews.SourceEP(util.GatorEnforcementPoint)) if err != nil { return nil, fmt.Errorf("reviewing expanded resource %v %s/%s: %w", resultant.Obj.GroupVersionKind(), resultant.Obj.GetNamespace(), resultant.Obj.GetName(), err) diff --git a/pkg/gator/test/test_test.go b/pkg/gator/test/test_test.go index 95a0d18ac39..a5724cafc14 100644 --- a/pkg/gator/test/test_test.go +++ b/pkg/gator/test/test_test.go @@ -19,6 +19,7 @@ import ( var ( templateNeverValidate *unstructured.Unstructured constraintNeverValidate *unstructured.Unstructured + constraintGatorValidate *unstructured.Unstructured constraintReferential *unstructured.Unstructured object *unstructured.Unstructured objectReferentialInventory *unstructured.Unstructured @@ -37,6 +38,11 @@ func init() { panic(err) } + constraintGatorValidate, err = readUnstructured([]byte(fixtures.ConstraintGatorValidate)) + if err != nil { + panic(err) + } + constraintReferential, err = readUnstructured([]byte(fixtures.ConstraintReferential)) if err != nil { panic(err) @@ -180,6 +186,53 @@ func TestTest(t *testing.T) { }, err: constraintclient.ErrMissingConstraintTemplate, }, + { + name: "constraint with gator EP", + inputs: []string{ + fixtures.TemplateNeverValidate, + fixtures.ConstraintGatorValidate, + fixtures.Object, + }, + want: []*GatorResult{ + { + Result: types.Result{ + Target: target.Name, + Msg: "never validate", + Constraint: constraintGatorValidate, + EnforcementAction: "scoped", + ScopedEnforcementActions: []string{"deny", "warn"}, + }, + }, + { + Result: types.Result{ + Target: target.Name, + Msg: "never validate", + Constraint: constraintGatorValidate, + EnforcementAction: "scoped", + ScopedEnforcementActions: []string{"deny", "warn"}, + }, + }, + { + Result: types.Result{ + Target: target.Name, + Msg: "never validate", + Constraint: constraintGatorValidate, + EnforcementAction: "scoped", + ScopedEnforcementActions: []string{"deny", "warn"}, + }, + }, + }, + cmpOption: ignoreGatorResultFields(), + }, + { + name: "constraint with audit EP", + inputs: []string{ + fixtures.TemplateNeverValidate, + fixtures.ConstraintAuditValidate, + fixtures.Object, + }, + cmpOption: ignoreGatorResultFields(), + }, } for _, tc := range tcs { diff --git a/pkg/gator/verify/runner.go b/pkg/gator/verify/runner.go index 0597f6237a4..5fca9af7d20 100644 --- a/pkg/gator/verify/runner.go +++ b/pkg/gator/verify/runner.go @@ -9,6 +9,7 @@ import ( "time" "github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints" + "github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews" "github.com/open-policy-agent/frameworks/constraint/pkg/types" "github.com/open-policy-agent/gatekeeper/v3/apis" "github.com/open-policy-agent/gatekeeper/v3/pkg/gator" @@ -326,7 +327,7 @@ func (r *Runner) runReview(ctx context.Context, newClient func() (gator.Client, Object: *toReview, Source: mutationtypes.SourceTypeOriginal, } - return c.Review(ctx, au) + return c.Review(ctx, au, reviews.SourceEP(util.GatorEnforcementPoint)) } func (r *Runner) validateAndReviewAdmissionReviewRequest(ctx context.Context, c gator.Client, toReview *unstructured.Unstructured) (*types.Responses, error) { @@ -369,7 +370,7 @@ func (r *Runner) validateAndReviewAdmissionReviewRequest(ctx context.Context, c Source: mutationtypes.SourceTypeOriginal, } - return c.Review(ctx, arr) + return c.Review(ctx, arr, reviews.SourceEP(util.GatorEnforcementPoint)) } func (r *Runner) addInventory(ctx context.Context, c gator.Client, suiteDir, inventoryPath string) error { diff --git a/pkg/logging/logging.go b/pkg/logging/logging.go index d9f4ec81b13..3821aca2d73 100644 --- a/pkg/logging/logging.go +++ b/pkg/logging/logging.go @@ -9,32 +9,33 @@ import ( // Log keys. const ( - Process = "process" - Details = "details" - EventType = "event_type" - TemplateName = "template_name" - ConstraintNamespace = "constraint_namespace" - ConstraintName = "constraint_name" - ConstraintGroup = "constraint_group" - ConstraintKind = "constraint_kind" - ConstraintAPIVersion = "constraint_api_version" - ConstraintStatus = "constraint_status" - ConstraintAction = "constraint_action" - ConstraintAnnotations = "constraint_annotations" - AuditID = "audit_id" - ConstraintViolations = "constraint_violations" - ResourceGroup = "resource_group" - ResourceKind = "resource_kind" - ResourceLabels = "resource_labels" - ResourceAPIVersion = "resource_api_version" - ResourceNamespace = "resource_namespace" - ResourceName = "resource_name" - ResourceSourceType = "resource_source_type" - RequestUsername = "request_username" - MutationApplied = "mutation_applied" - Mutator = "mutator" - DebugLevel = 1 // r.log.Debug(foo) == r.log.V(logging.DebugLevel).Info(foo) - ExecutionStats = "execution_stats" + Process = "process" + Details = "details" + EventType = "event_type" + TemplateName = "template_name" + ConstraintNamespace = "constraint_namespace" + ConstraintName = "constraint_name" + ConstraintGroup = "constraint_group" + ConstraintKind = "constraint_kind" + ConstraintAPIVersion = "constraint_api_version" + ConstraintStatus = "constraint_status" + ConstraintAction = "constraint_action" + ConstraintEnforcementActions = "constraint_enforcement_actions" + ConstraintAnnotations = "constraint_annotations" + AuditID = "audit_id" + ConstraintViolations = "constraint_violations" + ResourceGroup = "resource_group" + ResourceKind = "resource_kind" + ResourceLabels = "resource_labels" + ResourceAPIVersion = "resource_api_version" + ResourceNamespace = "resource_namespace" + ResourceName = "resource_name" + ResourceSourceType = "resource_source_type" + RequestUsername = "request_username" + MutationApplied = "mutation_applied" + Mutator = "mutator" + DebugLevel = 1 // r.log.Debug(foo) == r.log.V(logging.DebugLevel).Info(foo) + ExecutionStats = "execution_stats" ) func LogStatsEntries(client *constraintclient.Client, logger logr.Logger, entries []*instrumentation.StatsEntry, msg string) { diff --git a/pkg/target/target_integration_test.go b/pkg/target/target_integration_test.go index dee7d32558e..d713bc19460 100644 --- a/pkg/target/target_integration_test.go +++ b/pkg/target/target_integration_test.go @@ -7,10 +7,11 @@ import ( templatesv1beta1 "github.com/open-policy-agent/frameworks/constraint/pkg/apis/templates/v1beta1" constraintclient "github.com/open-policy-agent/frameworks/constraint/pkg/client" - "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers" "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/rego" + "github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews" "github.com/open-policy-agent/frameworks/constraint/pkg/core/templates" api "github.com/open-policy-agent/gatekeeper/v3/apis" + "github.com/open-policy-agent/gatekeeper/v3/pkg/util" admissionv1 "k8s.io/api/admission/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -427,7 +428,7 @@ func TestConstraintEnforcement(t *testing.T) { t.Fatalf("unable to set up Driver: %v", err) } - c, err := constraintclient.NewClient(constraintclient.Targets(target), constraintclient.Driver(driver)) + c, err := constraintclient.NewClient(constraintclient.Targets(target), constraintclient.Driver(driver), constraintclient.EnforcementPoints(util.AuditEnforcementPoint)) if err != nil { t.Fatalf("unable to set up OPA client: %s", err) } @@ -468,7 +469,7 @@ func TestConstraintEnforcement(t *testing.T) { } fullReq := &AugmentedReview{Namespace: tc.ns, AdmissionRequest: req} - res, err := c.Review(ctx, fullReq, drivers.Tracing(true)) + res, err := c.Review(ctx, fullReq, reviews.SourceEP(util.AuditEnforcementPoint), reviews.Tracing(true)) if err != nil { t.Errorf("Error reviewing request: %s", err) } @@ -497,7 +498,7 @@ func TestConstraintEnforcement(t *testing.T) { } fullReq2 := &AugmentedReview{Namespace: tc.ns, AdmissionRequest: req2} - res2, err := c.Review(ctx, fullReq2, drivers.Tracing(true)) + res2, err := c.Review(ctx, fullReq2, reviews.SourceEP(util.AuditEnforcementPoint), reviews.Tracing(true)) if err != nil { t.Errorf("Error reviewing OldObject request: %s", err) } @@ -510,7 +511,7 @@ func TestConstraintEnforcement(t *testing.T) { } fullReq3 := &AugmentedUnstructured{Namespace: tc.ns, Object: *tc.obj} - res3, err := c.Review(ctx, fullReq3, drivers.Tracing(true)) + res3, err := c.Review(ctx, fullReq3, reviews.SourceEP(util.AuditEnforcementPoint), reviews.Tracing(true)) if err != nil { t.Errorf("Error reviewing AugmentedUnstructured request: %s", err) } diff --git a/pkg/util/enforcement_action.go b/pkg/util/enforcement_action.go index 555fbe2d2e4..917440dcf92 100644 --- a/pkg/util/enforcement_action.go +++ b/pkg/util/enforcement_action.go @@ -1,9 +1,11 @@ package util import ( + "encoding/json" "errors" "fmt" + apiconstraints "github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) @@ -15,13 +17,30 @@ const ( Deny EnforcementAction = "deny" Dryrun EnforcementAction = "dryrun" Warn EnforcementAction = "warn" + Scoped EnforcementAction = "scoped" Unrecognized EnforcementAction = "unrecognized" ) -var supportedEnforcementActions = []EnforcementAction{Deny, Dryrun, Warn} +const ( + // WebhookEnforcementPoint is the enforcement point for admission. + WebhookEnforcementPoint = "validation.gatekeeper.sh" + + // AuditEnforcementPoint is the enforcement point for audit. + AuditEnforcementPoint = "audit.gatekeeper.sh" + + // GatorEnforcementPoint is the enforcement point for gator cli. + GatorEnforcementPoint = "gator.gatekeeper.sh" + + // VAP enforcement point for ValidatingAdmissionPolicy. + VAPEnforcementPoint = "vap.k8s.io" +) + +var supportedEnforcementActions = []EnforcementAction{Deny, Dryrun, Warn, Scoped} + +var supportedScopedActions = []EnforcementAction{Deny, Dryrun, Warn} // KnownEnforcementActions are all defined EnforcementActions. -var KnownEnforcementActions = []EnforcementAction{Deny, Dryrun, Warn, Unrecognized} +var KnownEnforcementActions = []EnforcementAction{Deny, Dryrun, Warn, Scoped, Unrecognized} // ErrEnforcementAction indicates the passed EnforcementAction is not valid. var ErrEnforcementAction = errors.New("unrecognized enforcementAction") @@ -30,14 +49,69 @@ var ErrEnforcementAction = errors.New("unrecognized enforcementAction") // spec.enforcementAction field as it was not a string. var ErrInvalidSpecEnforcementAction = errors.New("spec.enforcementAction must be a string") -func ValidateEnforcementAction(input EnforcementAction) error { - for _, n := range supportedEnforcementActions { - if input == n { - return nil +var ErrInvalidSpecScopedEnforcementAction = errors.New("spec.scopedEnforcementAction must be in the format of []{action: string, enforcementPoints: []{name: string}}") + +func ValidateEnforcementAction(input EnforcementAction, item map[string]interface{}) error { + switch input { + case Scoped: + return ValidateScopedEnforcementAction(item) + case Dryrun, Deny, Warn: + return nil + default: + return fmt.Errorf("%w: %q is not within the supported list %v", + ErrEnforcementAction, input, supportedEnforcementActions) + } +} + +func ValidateScopedEnforcementAction(item map[string]interface{}) error { + obj, err := GetScopedEnforcementAction(item) + if err != nil { + return fmt.Errorf("error fetching scopedEnforcementActions: %w", err) + } + + // validating scopedEnforcementActions + for _, scopedEnforcementAction := range *obj { + if scopedEnforcementAction.Action == "" { + return ErrInvalidSpecEnforcementAction + } + switch EnforcementAction(scopedEnforcementAction.Action) { + case Dryrun, Deny, Warn: + default: + return fmt.Errorf("%w: %q is not within the supported list %v", ErrEnforcementAction, scopedEnforcementAction.Action, supportedScopedActions) + } + if len(scopedEnforcementAction.EnforcementPoints) == 0 { + return ErrInvalidSpecEnforcementAction + } + for _, enforcementPoint := range scopedEnforcementAction.EnforcementPoints { + if enforcementPoint.Name == "" { + return ErrInvalidSpecEnforcementAction + } } } - return fmt.Errorf("%w: %q is not within the supported list %v", - ErrEnforcementAction, input, supportedEnforcementActions) + return nil +} + +func GetScopedEnforcementAction(item map[string]interface{}) (*[]apiconstraints.ScopedEnforcementAction, error) { + scopedEnforcementActions, found, err := unstructured.NestedFieldNoCopy(item, "spec", "scopedEnforcementActions") + if err != nil { + return nil, fmt.Errorf("error fetching scopedEnforcementActions: %w", err) + } + if !found { + return nil, fmt.Errorf("scopedEnforcementActions is required") + } + return convertToScopedEnforcementActions(scopedEnforcementActions) +} + +func convertToScopedEnforcementActions(object interface{}) (*[]apiconstraints.ScopedEnforcementAction, error) { + j, err := json.Marshal(object) + if err != nil { + return nil, fmt.Errorf("could not convert unknown object to JSON: %w", err) + } + obj := []apiconstraints.ScopedEnforcementAction{} + if err := json.Unmarshal(j, &obj); err != nil { + return nil, fmt.Errorf("Could not convert JSON to scopedEnforcementActions: %w", err) + } + return &obj, nil } func GetEnforcementAction(item map[string]interface{}) (EnforcementAction, error) { @@ -50,10 +124,36 @@ func GetEnforcementAction(item map[string]interface{}) (EnforcementAction, error if enforcementAction == "" { enforcementAction = Deny } - // validating enforcement action - if it is not deny or dryrun, we are classifying as unrecognized - if err := ValidateEnforcementAction(enforcementAction); err != nil { + // validating enforcement action - if it is not deny or dryrun or scoped, we are classifying as unrecognized + switch enforcementAction { + case Dryrun, Deny, Warn, Scoped: + return enforcementAction, nil + default: enforcementAction = Unrecognized } return enforcementAction, nil } + +func ScopedActionForEP(enforcementPoint string, u *unstructured.Unstructured) ([]string, error) { + enforcementActions := []string{} + scopedEnforcementActions, err := GetScopedEnforcementAction(u.Object) + if err != nil { + return nil, err + } + for _, scopedEnforcementAction := range *scopedEnforcementActions { + if enforcementPointEnabled(scopedEnforcementAction, enforcementPoint) { + enforcementActions = append(enforcementActions, scopedEnforcementAction.Action) + } + } + return enforcementActions, nil +} + +func enforcementPointEnabled(scopedEnforcementAction apiconstraints.ScopedEnforcementAction, enforcementPoint string) bool { + for _, ep := range scopedEnforcementAction.EnforcementPoints { + if ep.Name == enforcementPoint || ep.Name == "*" { + return true + } + } + return false +} diff --git a/pkg/util/enforcement_action_test.go b/pkg/util/enforcement_action_test.go index cacc41f0eff..e0958500027 100644 --- a/pkg/util/enforcement_action_test.go +++ b/pkg/util/enforcement_action_test.go @@ -2,26 +2,78 @@ package util import ( "errors" + "reflect" "testing" + + apiconstraints "github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) func TestValidateEnforcementAction(t *testing.T) { testCases := []struct { - name string - action EnforcementAction - wantErr error + name string + action EnforcementAction + wantErr error + constraint map[string]interface{} }{ { - name: "empty string", - action: "", - wantErr: ErrEnforcementAction, + name: "empty string", + action: "", + wantErr: ErrEnforcementAction, + constraint: nil, + }, + { + action: "notsupported", + wantErr: ErrEnforcementAction, + constraint: nil, }, { - action: "notsupported", + action: Dryrun, + constraint: nil, + }, + { + name: "invalid spec.scopedEnforcementAction", + action: Scoped, wantErr: ErrEnforcementAction, + constraint: map[string]interface{}{ + "spec": map[string]interface{}{ + "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ + { + Action: "deny", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "audit", + }, + }, + }, + { + Action: "test", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "audit", + }, + }, + }, + }, + }, + }, }, { - action: Dryrun, + action: Scoped, + constraint: map[string]interface{}{ + "spec": map[string]interface{}{ + "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ + { + Action: "deny", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "audit", + }, + }, + }, + }, + }, + }, }, } @@ -30,7 +82,7 @@ func TestValidateEnforcementAction(t *testing.T) { tc.name = string(tc.action) } t.Run(tc.name, func(t *testing.T) { - err := ValidateEnforcementAction(tc.action) + err := ValidateEnforcementAction(tc.action, tc.constraint) if !errors.Is(err, tc.wantErr) { t.Errorf("got ValidateEnforcementAction(%q) == %v, want %v", tc.action, err, tc.wantErr) @@ -92,3 +144,229 @@ func TestGetEnforcementAction(t *testing.T) { }) } } + +func TestGetScopedEnforcementAction(t *testing.T) { + testCases := []struct { + name string + item map[string]interface{} + expectedError error + expectedObj *[]apiconstraints.ScopedEnforcementAction + }{ + { + name: "valid scopedEnforcementActions", + item: map[string]interface{}{ + "spec": map[string]interface{}{ + "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ + { + Action: "deny", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "audit", + }, + }, + }, + }, + }, + }, + expectedError: nil, + expectedObj: &[]apiconstraints.ScopedEnforcementAction{ + { + Action: "deny", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "audit", + }, + }, + }, + }, + }, + { + name: "missing scopedEnforcementActions", + item: map[string]interface{}{ + "spec": map[string]interface{}{}, + }, + expectedError: errors.New("scopedEnforcementActions is required"), + expectedObj: nil, + }, + { + name: "invalid scopedEnforcementActions", + item: map[string]interface{}{ + "spec": map[string]interface{}{ + "scopedEnforcementActions": "invalid", + }, + }, + expectedError: errors.New("Could not convert JSON to scopedEnforcementActions: json: cannot unmarshal string into Go value of type []constraints.ScopedEnforcementAction"), + expectedObj: nil, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + obj, err := GetScopedEnforcementAction(tc.item) + if err != nil && tc.expectedError != nil && !errors.Is(err, tc.expectedError) && (err.Error() != tc.expectedError.Error()) { + t.Errorf("got GetScopedEnforcementAction() error = %v, want %v", err, tc.expectedError) + } + if !reflect.DeepEqual(obj, tc.expectedObj) { + t.Errorf("got GetScopedEnforcementAction() = %v, want %v", obj, tc.expectedObj) + } + }) + } +} + +func TestScopedActionForEP(t *testing.T) { + testCases := []struct { + name string + enforcementPoint string + item map[string]interface{} + expectedActions []string + expectedError error + }{ + { + name: "valid enforcement point", + enforcementPoint: "audit", + item: map[string]interface{}{ + "spec": map[string]interface{}{ + "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ + { + Action: "deny", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "audit", + }, + }, + }, + { + Action: "warn", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "webhook", + }, + }, + }, + }, + }, + }, + expectedActions: []string{"deny"}, + expectedError: nil, + }, + { + name: "multiple enforcement points", + enforcementPoint: "webhook", + item: map[string]interface{}{ + "spec": map[string]interface{}{ + "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ + { + Action: "deny", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "audit", + }, + { + Name: "webhook", + }, + }, + }, + { + Action: "warn", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "webhook", + }, + }, + }, + }, + }, + }, + expectedActions: []string{"deny", "warn"}, + expectedError: nil, + }, + { + name: "no matching enforcement point", + enforcementPoint: "audit", + item: map[string]interface{}{ + "spec": map[string]interface{}{ + "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ + { + Action: "deny", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "webhook", + }, + }, + }, + { + Action: "warn", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "webhook", + }, + }, + }, + }, + }, + }, + expectedActions: []string{}, + expectedError: nil, + }, + { + name: "wildcard enforcement point", + enforcementPoint: "audit", + item: map[string]interface{}{ + "spec": map[string]interface{}{ + "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ + { + Action: "deny", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "*", + }, + }, + }, + { + Action: "warn", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "webhook", + }, + }, + }, + }, + }, + }, + expectedActions: []string{"deny"}, + expectedError: nil, + }, + { + name: "missing scopedEnforcementActions", + enforcementPoint: "audit", + item: map[string]interface{}{ + "spec": map[string]interface{}{}, + }, + expectedActions: nil, + expectedError: nil, + }, + { + name: "invalid scopedEnforcementActions", + enforcementPoint: "audit", + item: map[string]interface{}{ + "spec": map[string]interface{}{ + "scopedEnforcementActions": "invalid", + }, + }, + expectedActions: nil, + expectedError: errors.New("Could not convert JSON to scopedEnforcementActions: json: cannot unmarshal string into Go value of type []constraints.ScopedEnforcementAction"), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + actions, err := ScopedActionForEP(tc.enforcementPoint, &unstructured.Unstructured{Object: tc.item}) + if !reflect.DeepEqual(actions, tc.expectedActions) { + t.Errorf("got ScopedActionForEP() = %v, want %v", actions, tc.expectedActions) + } + if err != nil && tc.expectedError != nil && !errors.Is(err, tc.expectedError) && (err.Error() != tc.expectedError.Error()) { + t.Errorf("got ScopedActionForEP() error = %v, want %v", err, tc.expectedError) + } + }) + } +} diff --git a/pkg/webhook/policy.go b/pkg/webhook/policy.go index e6c36d46ac1..ec228a47d33 100644 --- a/pkg/webhook/policy.go +++ b/pkg/webhook/policy.go @@ -29,7 +29,7 @@ import ( "github.com/open-policy-agent/cert-controller/pkg/rotator" externaldataUnversioned "github.com/open-policy-agent/frameworks/constraint/pkg/apis/externaldata/unversioned" constraintclient "github.com/open-policy-agent/frameworks/constraint/pkg/client" - "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers" + "github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews" "github.com/open-policy-agent/frameworks/constraint/pkg/core/templates" "github.com/open-policy-agent/frameworks/constraint/pkg/externaldata" rtypes "github.com/open-policy-agent/frameworks/constraint/pkg/types" @@ -258,72 +258,78 @@ func (h *validationHandler) getValidationMessages(res []*rtypes.Result, req *adm } } for _, r := range res { - if err := util.ValidateEnforcementAction(util.EnforcementAction(r.EnforcementAction)); err != nil { - continue + actions := r.ScopedEnforcementActions + if len(actions) == 0 { + actions = []string{r.EnforcementAction} } - if *logDenies { - h.log.WithValues( - logging.Process, "admission", - logging.Details, r.Metadata["details"], - logging.EventType, "violation", - logging.ConstraintName, r.Constraint.GetName(), - logging.ConstraintGroup, r.Constraint.GroupVersionKind().Group, - logging.ConstraintAPIVersion, r.Constraint.GroupVersionKind().Version, - logging.ConstraintKind, r.Constraint.GetKind(), - logging.ConstraintAction, r.EnforcementAction, - logging.ResourceGroup, req.AdmissionRequest.Kind.Group, - logging.ResourceAPIVersion, req.AdmissionRequest.Kind.Version, - logging.ResourceKind, req.AdmissionRequest.Kind.Kind, - logging.ResourceNamespace, req.AdmissionRequest.Namespace, - logging.ResourceName, resourceName, - logging.RequestUsername, req.AdmissionRequest.UserInfo.Username, - ).Info( - fmt.Sprintf("denied admission: %s", r.Msg)) - } - if *emitAdmissionEvents { - annotations := map[string]string{ - logging.Process: "admission", - logging.EventType: "violation", - logging.ConstraintName: r.Constraint.GetName(), - logging.ConstraintGroup: r.Constraint.GroupVersionKind().Group, - logging.ConstraintAPIVersion: r.Constraint.GroupVersionKind().Version, - logging.ConstraintKind: r.Constraint.GetKind(), - logging.ConstraintAction: r.EnforcementAction, - logging.ResourceGroup: req.AdmissionRequest.Kind.Group, - logging.ResourceAPIVersion: req.AdmissionRequest.Kind.Version, - logging.ResourceKind: req.AdmissionRequest.Kind.Kind, - logging.ResourceNamespace: req.AdmissionRequest.Namespace, - logging.ResourceName: resourceName, - logging.RequestUsername: req.AdmissionRequest.UserInfo.Username, + for _, action := range actions { + if err := util.ValidateEnforcementAction(util.EnforcementAction(action), r.Constraint.Object); err != nil { + continue } - var eventMsg, reason string - switch r.EnforcementAction { - case string(util.Dryrun): - eventMsg = "Dryrun violation" - reason = "DryrunViolation" - case string(util.Warn): - eventMsg = "Admission webhook \"validation.gatekeeper.sh\" raised a warning for this request" - reason = "WarningAdmission" - default: - eventMsg = "Admission webhook \"validation.gatekeeper.sh\" denied request" - reason = "FailedAdmission" + if *logDenies { + h.log.WithValues( + logging.Process, "admission", + logging.Details, r.Metadata["details"], + logging.EventType, "violation", + logging.ConstraintName, r.Constraint.GetName(), + logging.ConstraintGroup, r.Constraint.GroupVersionKind().Group, + logging.ConstraintAPIVersion, r.Constraint.GroupVersionKind().Version, + logging.ConstraintKind, r.Constraint.GetKind(), + logging.ConstraintAction, r.EnforcementAction, + logging.ResourceGroup, req.AdmissionRequest.Kind.Group, + logging.ResourceAPIVersion, req.AdmissionRequest.Kind.Version, + logging.ResourceKind, req.AdmissionRequest.Kind.Kind, + logging.ResourceNamespace, req.AdmissionRequest.Namespace, + logging.ResourceName, resourceName, + logging.RequestUsername, req.AdmissionRequest.UserInfo.Username, + ).Info( + fmt.Sprintf("denied admission: %s", r.Msg)) } + if *emitAdmissionEvents { + annotations := map[string]string{ + logging.Process: "admission", + logging.EventType: "violation", + logging.ConstraintName: r.Constraint.GetName(), + logging.ConstraintGroup: r.Constraint.GroupVersionKind().Group, + logging.ConstraintAPIVersion: r.Constraint.GroupVersionKind().Version, + logging.ConstraintKind: r.Constraint.GetKind(), + logging.ConstraintAction: action, + logging.ResourceGroup: req.AdmissionRequest.Kind.Group, + logging.ResourceAPIVersion: req.AdmissionRequest.Kind.Version, + logging.ResourceKind: req.AdmissionRequest.Kind.Kind, + logging.ResourceNamespace: req.AdmissionRequest.Namespace, + logging.ResourceName: resourceName, + logging.RequestUsername: req.AdmissionRequest.UserInfo.Username, + } + var eventMsg, reason string + switch action { + case string(util.Dryrun): + eventMsg = "Dryrun violation" + reason = "DryrunViolation" + case string(util.Warn): + eventMsg = "Admission webhook \"validation.gatekeeper.sh\" raised a warning for this request" + reason = "WarningAdmission" + default: + eventMsg = "Admission webhook \"validation.gatekeeper.sh\" denied request" + reason = "FailedAdmission" + } - ref := getViolationRef(h.gkNamespace, req.AdmissionRequest.Kind.Kind, resourceName, obj.GetNamespace(), obj.GetResourceVersion(), obj.GetUID(), r.Constraint.GetKind(), r.Constraint.GetName(), r.Constraint.GetNamespace(), *admissionEventsInvolvedNamespace) + ref := getViolationRef(h.gkNamespace, req.AdmissionRequest.Kind.Kind, resourceName, obj.GetNamespace(), obj.GetResourceVersion(), obj.GetUID(), r.Constraint.GetKind(), r.Constraint.GetName(), r.Constraint.GetNamespace(), *admissionEventsInvolvedNamespace) - if *admissionEventsInvolvedNamespace { - h.eventRecorder.AnnotatedEventf(ref, annotations, corev1.EventTypeWarning, reason, "%s, Constraint: %s, Message: %s", eventMsg, r.Constraint.GetName(), r.Msg) - } else { - h.eventRecorder.AnnotatedEventf(ref, annotations, corev1.EventTypeWarning, reason, "%s, Resource Namespace: %s, Constraint: %s, Message: %s", eventMsg, req.AdmissionRequest.Namespace, r.Constraint.GetName(), r.Msg) + if *admissionEventsInvolvedNamespace { + h.eventRecorder.AnnotatedEventf(ref, annotations, corev1.EventTypeWarning, reason, "%s, Constraint: %s, Message: %s", eventMsg, r.Constraint.GetName(), r.Msg) + } else { + h.eventRecorder.AnnotatedEventf(ref, annotations, corev1.EventTypeWarning, reason, "%s, Resource Namespace: %s, Constraint: %s, Message: %s", eventMsg, req.AdmissionRequest.Namespace, r.Constraint.GetName(), r.Msg) + } } - } - if r.EnforcementAction == string(util.Deny) { - denyMsgs = append(denyMsgs, fmt.Sprintf("[%s] %s", r.Constraint.GetName(), r.Msg)) - } + if action == string(util.Deny) { + denyMsgs = append(denyMsgs, fmt.Sprintf("[%s] %s", r.Constraint.GetName(), r.Msg)) + } - if r.EnforcementAction == string(util.Warn) { - warnMsgs = append(warnMsgs, fmt.Sprintf("[%s] %s", r.Constraint.GetName(), r.Msg)) + if action == string(util.Warn) { + warnMsgs = append(warnMsgs, fmt.Sprintf("[%s] %s", r.Constraint.GetName(), r.Msg)) + } } } return denyMsgs, warnMsgs @@ -413,7 +419,7 @@ func (h *validationHandler) validateConstraint(req *admission.Request) (bool, er enforcementAction := util.EnforcementAction(enforcementActionString) if found && enforcementAction != "" { if !*disableEnforcementActionValidation { - err = util.ValidateEnforcementAction(enforcementAction) + err = util.ValidateEnforcementAction(enforcementAction, obj.Object) if err != nil { return false, err } @@ -599,7 +605,7 @@ func (h *validationHandler) reviewRequest(ctx context.Context, req *admission.Re } func (h *validationHandler) review(ctx context.Context, review interface{}, trace bool, dump bool) (*rtypes.Responses, error) { - resp, err := h.opa.Review(ctx, review, drivers.Tracing(trace), drivers.Stats(*logStatsAdmission)) + resp, err := h.opa.Review(ctx, review, reviews.SourceEP(util.WebhookEnforcementPoint), reviews.Tracing(trace), reviews.Stats(*logStatsAdmission), reviews.SourceEP(util.WebhookEnforcementPoint)) if resp != nil && trace { h.log.Info(resp.TraceDump()) } diff --git a/pkg/webhook/policy_test.go b/pkg/webhook/policy_test.go index 66945de36a5..f57ba27ebb1 100644 --- a/pkg/webhook/policy_test.go +++ b/pkg/webhook/policy_test.go @@ -19,6 +19,7 @@ import ( "github.com/open-policy-agent/gatekeeper/v3/pkg/fakes" "github.com/open-policy-agent/gatekeeper/v3/pkg/mutation" "github.com/open-policy-agent/gatekeeper/v3/pkg/target" + "github.com/open-policy-agent/gatekeeper/v3/pkg/util" "github.com/open-policy-agent/gatekeeper/v3/pkg/wildcard" testclients "github.com/open-policy-agent/gatekeeper/v3/test/clients" "github.com/stretchr/testify/require" @@ -204,7 +205,7 @@ func makeOpaClient() (*constraintclient.Client, error) { return nil, err } - c, err := constraintclient.NewClient(constraintclient.Targets(t), constraintclient.Driver(driver)) + c, err := constraintclient.NewClient(constraintclient.Targets(t), constraintclient.Driver(driver), constraintclient.EnforcementPoints(util.WebhookEnforcementPoint)) if err != nil { return nil, err } diff --git a/test/bats/test.bats b/test/bats/test.bats index 5e67e773e23..6fd6186cb8e 100644 --- a/test/bats/test.bats +++ b/test/bats/test.bats @@ -78,16 +78,23 @@ teardown_file() { wait_for_process ${WAIT_TIME} ${SLEEP_TIME} "kubectl get ValidatingAdmissionPolicy gatekeeper-k8srequiredlabelsvap" + wait_for_process ${WAIT_TIME} ${SLEEP_TIME} "kubectl apply -f ${BATS_TESTS_DIR}/constraints/all_ns_must_have_label_provided_vapbinding_scoped.yaml" + wait_for_process ${WAIT_TIME} ${SLEEP_TIME} "kubectl apply -f ${BATS_TESTS_DIR}/constraints/all_ns_must_have_label_provided_vapbinding.yaml" wait_for_process ${WAIT_TIME} ${SLEEP_TIME} "kubectl get ValidatingAdmissionPolicyBinding gatekeeper-all-must-have-label" + + wait_for_process ${WAIT_TIME} ${SLEEP_TIME} "kubectl get ValidatingAdmissionPolicyBinding gatekeeper-all-must-have-label-scoped" run kubectl apply -f ${BATS_TESTS_DIR}/bad/bad_ns.yaml + assert_match 'Warning' "${output}" assert_match 'denied' "${output}" assert_failure kubectl apply -f ${BATS_TESTS_DIR}/good/good_ns.yaml kubectl delete --ignore-not-found -f ${BATS_TESTS_DIR}/good/good_ns.yaml kubectl delete --ignore-not-found -f ${BATS_TESTS_DIR}/bad/bad_ns.yaml + kubectl delete --ignore-not-found -f ${BATS_TESTS_DIR}/constraints/all_ns_must_have_label_provided_vapbinding.yaml + kubectl delete --ignore-not-found -f ${BATS_TESTS_DIR}/constraints/all_ns_must_have_label_provided_vapbinding_scoped.yaml wait_for_process ${WAIT_TIME} ${SLEEP_TIME} "kubectl delete --ignore-not-found -f ${BATS_TESTS_DIR}/templates/k8srequiredlabels_template_vap.yaml" fi @@ -153,6 +160,9 @@ teardown_file() { kubectl apply -f ${BATS_TESTS_DIR}/templates/k8srequiredlabels_template.yaml wait_for_process ${WAIT_TIME} ${SLEEP_TIME} "kubectl apply -f ${BATS_TESTS_DIR}/constraints/all_cm_must_have_gatekeeper_audit.yaml" + wait_for_process ${WAIT_TIME} ${SLEEP_TIME} "kubectl apply -f ${BATS_TESTS_DIR}/constraints/all_cm_must_have_gatekeeper_scoped_audit.yaml" + wait_for_process ${WAIT_TIME} ${SLEEP_TIME} "kubectl apply -f ${BATS_TESTS_DIR}/constraints/all_cm_must_have_gatekeeper_scoped.yaml" + wait_for_process ${WAIT_TIME} ${SLEEP_TIME} "kubectl apply -f ${BATS_TESTS_DIR}/constraints/all_cm_must_have_gatekeeper_scoped_webhook.yaml" } @test "no ignore label unless namespace is exempt test" { @@ -192,6 +202,15 @@ teardown_file() { kubectl apply -f ${BATS_TESTS_DIR}/bad/bad_cm.yaml kubectl delete --ignore-not-found -f ${BATS_TESTS_DIR}/bad/bad_cm.yaml + + # deploying a violation to get rejected with scoped enforcement actions + run kubectl apply -f ${BATS_TESTS_DIR}/bad/bad_cm_scoped.yaml + + assert_match 'Warning' "${output}" + assert_match 'denied the request' "${output}" + assert_failure + + kubectl delete --ignore-not-found -f ${BATS_TESTS_DIR}/bad/bad_cm_scoped.yaml } @test "container limits test" { @@ -252,6 +271,88 @@ __required_labels_audit_test() { echo "Audit entry count is ${audit_entries}, wanted ${expected}" return 3 fi + + local cstr="$(kubectl get k8srequiredlabels.constraints.gatekeeper.sh cm-must-have-gk-scoped -ojson)" + if [[ $? -ne 0 ]]; then + echo "error retrieving constraint" + return 1 + fi + + echo "${cstr}" + + local total_violations=$(echo "${cstr}" | jq '.status.totalViolations') + if [[ "${total_violations}" -ne "${expected}" ]]; then + echo "totalViolations is ${total_violations}, wanted ${expected}" + return 2 + fi + + local audit_entries=$(echo "${cstr}" | jq '.status.violations | length') + if [[ "${audit_entries}" -ne "${expected}" ]]; then + echo "Audit entry count is ${audit_entries}, wanted ${expected}" + return 3 + fi + + local violations=$(echo "${cstr}" | jq -r '.status.violations[].enforcementAction') + local match=true + + for violation in $violations; do + if [[ "${violation}" != "deny" ]]; then + echo "Mismatch found: Enforcement action is ${violation}, expected deny" + match=false + fi + done + + if [[ "${match}" == "false" ]]; then + return 3 + fi + + local cstr="$(kubectl get k8srequiredlabels.constraints.gatekeeper.sh cm-must-have-gk-scoped-audit -ojson)" + if [[ $? -ne 0 ]]; then + echo "error retrieving constraint" + return 1 + fi + + echo "${cstr}" + + local total_violations=$(echo "${cstr}" | jq '.status.totalViolations') + if [[ "${total_violations}" -ne "${expected}" ]]; then + echo "totalViolations is ${total_violations}, wanted ${expected}" + return 2 + fi + + local audit_entries=$(echo "${cstr}" | jq '.status.violations | length') + if [[ "${audit_entries}" -ne "${expected}" ]]; then + echo "Audit entry count is ${audit_entries}, wanted ${expected}" + return 3 + fi + + local violations=$(echo "${cstr}" | jq -r '.status.violations[].enforcementAction') + local match=true + + for violation in $violations; do + if [[ "${violation}" != "warn" ]]; then + echo "Mismatch found: Enforcement action is ${violation}, expected warn" + match=false + fi + done + + if [[ "${match}" == "false" ]]; then + return 3 + fi + + local cstr="$(kubectl get k8srequiredlabels.constraints.gatekeeper.sh cm-must-have-gk-scoped-webhook -ojson)" + if [[ $? -ne 0 ]]; then + echo "error retrieving constraint" + return 1 + fi + + echo "${cstr}" + + local total_violations=$(echo "${cstr}" | jq '.status.totalViolations') + if [[ "${total_violations}" -ne "0" ]]; then + echo "totalViolations is ${total_violations}, wanted 0" + return 2 + fi } @test "required labels audit test" { diff --git a/test/bats/tests/bad/bad_cm_audit.yaml b/test/bats/tests/bad/bad_cm_audit.yaml index c5e159bfa56..678e066616e 100644 --- a/test/bats/tests/bad/bad_cm_audit.yaml +++ b/test/bats/tests/bad/bad_cm_audit.yaml @@ -37,3 +37,43 @@ metadata: namespace: gatekeeper-test-playground labels: test.gatekeeper.sh/audit: "yes" +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: bad-cm-audit + namespace: gatekeeper-test-playground-scoped + labels: + test.gatekeeper.sh/audit: "yes" +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: bad-cm-audit-2 + namespace: gatekeeper-test-playground-scoped + labels: + test.gatekeeper.sh/audit: "yes" +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: bad-cm-audit-3 + namespace: gatekeeper-test-playground-scoped + labels: + test.gatekeeper.sh/audit: "yes" +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: bad-cm-audit-4 + namespace: gatekeeper-test-playground-scoped + labels: + test.gatekeeper.sh/audit: "yes" +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: bad-cm-audit-5 + namespace: gatekeeper-test-playground-scoped + labels: + test.gatekeeper.sh/audit: "yes" \ No newline at end of file diff --git a/test/bats/tests/bad/bad_cm_scoped.yaml b/test/bats/tests/bad/bad_cm_scoped.yaml new file mode 100644 index 00000000000..18205e193a5 --- /dev/null +++ b/test/bats/tests/bad/bad_cm_scoped.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + namespace: gatekeeper-test-playground-scoped + name: bad-cm-scoped + labels: + test.gatekeeper.sh/audit: "yes" diff --git a/test/bats/tests/constraints/all_cm_must_have_gatekeeper_scoped.yaml b/test/bats/tests/constraints/all_cm_must_have_gatekeeper_scoped.yaml new file mode 100644 index 00000000000..496e554787b --- /dev/null +++ b/test/bats/tests/constraints/all_cm_must_have_gatekeeper_scoped.yaml @@ -0,0 +1,25 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sRequiredLabels +metadata: + name: cm-must-have-gk-scoped + labels: + gatekeeper.sh/tests: "yes" +spec: + enforcementAction: scoped + scopedEnforcementActions: + - action: deny + enforcementPoints: + - name: audit.gatekeeper.sh + - action: warn + enforcementPoints: + - name: validation.gatekeeper.sh + match: + namespaces: ["gatekeeper-test-playground-scoped"] + labelSelector: + matchLabels: + test.gatekeeper.sh/audit: "yes" + kinds: + - apiGroups: [""] + kinds: ["ConfigMap"] + parameters: + labels: ["gatekeeper"] diff --git a/test/bats/tests/constraints/all_cm_must_have_gatekeeper_scoped_audit.yaml b/test/bats/tests/constraints/all_cm_must_have_gatekeeper_scoped_audit.yaml new file mode 100644 index 00000000000..905bd45bf58 --- /dev/null +++ b/test/bats/tests/constraints/all_cm_must_have_gatekeeper_scoped_audit.yaml @@ -0,0 +1,22 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sRequiredLabels +metadata: + name: cm-must-have-gk-scoped-audit + labels: + gatekeeper.sh/tests: "yes" +spec: + enforcementAction: scoped + scopedEnforcementActions: + - action: warn + enforcementPoints: + - name: audit.gatekeeper.sh + match: + namespaces: ["gatekeeper-test-playground-scoped"] + labelSelector: + matchLabels: + test.gatekeeper.sh/audit: "yes" + kinds: + - apiGroups: [""] + kinds: ["ConfigMap"] + parameters: + labels: ["gatekeeper"] diff --git a/test/bats/tests/constraints/all_cm_must_have_gatekeeper_scoped_webhook.yaml b/test/bats/tests/constraints/all_cm_must_have_gatekeeper_scoped_webhook.yaml new file mode 100644 index 00000000000..50d56bb4029 --- /dev/null +++ b/test/bats/tests/constraints/all_cm_must_have_gatekeeper_scoped_webhook.yaml @@ -0,0 +1,22 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sRequiredLabels +metadata: + name: cm-must-have-gk-scoped-webhook + labels: + gatekeeper.sh/tests: "yes" +spec: + enforcementAction: scoped + scopedEnforcementActions: + - action: deny + enforcementPoints: + - name: validation.gatekeeper.sh + match: + namespaces: ["gatekeeper-test-playground-scoped"] + labelSelector: + matchLabels: + test.gatekeeper.sh/audit: "yes" + kinds: + - apiGroups: [""] + kinds: ["ConfigMap"] + parameters: + labels: ["gatekeeper"] diff --git a/test/bats/tests/constraints/all_ns_must_have_label_provided_vapbinding_scoped.yaml b/test/bats/tests/constraints/all_ns_must_have_label_provided_vapbinding_scoped.yaml new file mode 100644 index 00000000000..13bcb3c0553 --- /dev/null +++ b/test/bats/tests/constraints/all_ns_must_have_label_provided_vapbinding_scoped.yaml @@ -0,0 +1,20 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sRequiredLabelsVap +metadata: + name: all-must-have-label-scoped +spec: + enforcementAction: scoped + scopedEnforcementActions: + - action: warn + enforcementPoints: + - name: vap.k8s.io + - name: validation.gatekeeper.sh + match: + kinds: + - apiGroups: [""] + kinds: ["Namespace"] + parameters: + message: "All namespaces must have an `owner` label that points to your company username" + labels: + - key: owner + allowedRegex: "^[a-zA-Z]+.agilebank.demo$" diff --git a/test/bats/tests/good/no_dupe_cm.yaml b/test/bats/tests/good/no_dupe_cm.yaml index 8c9d5c1f946..b215f9b7fc3 100644 --- a/test/bats/tests/good/no_dupe_cm.yaml +++ b/test/bats/tests/good/no_dupe_cm.yaml @@ -6,3 +6,12 @@ metadata: labels: gatekeeper: not_duplicated name: no-dupes +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: no-dupes + namespace: gatekeeper-test-playground-scoped + labels: + gatekeeper: not_duplicated + name: no-dupes diff --git a/test/bats/tests/good/playground_ns.yaml b/test/bats/tests/good/playground_ns.yaml index 4933ba3114c..aa296be93a7 100644 --- a/test/bats/tests/good/playground_ns.yaml +++ b/test/bats/tests/good/playground_ns.yaml @@ -2,3 +2,8 @@ apiVersion: v1 kind: Namespace metadata: name: gatekeeper-test-playground +--- +apiVersion: v1 +kind: Namespace +metadata: + name: gatekeeper-test-playground-scoped diff --git a/test/gator/verify/constraint_with_scopedEA.yaml b/test/gator/verify/constraint_with_scopedEA.yaml new file mode 100644 index 00000000000..fbfc77f43e6 --- /dev/null +++ b/test/gator/verify/constraint_with_scopedEA.yaml @@ -0,0 +1,19 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sFooIs +metadata: + name: foo-is-bar +spec: + enforcementAction: scoped + scopedEnforcementActions: + - action: deny + enforcementPoints: + - name: gator.gatekeeper.sh + - action: warn + enforcementPoints: + - name: validation.gatekeeper.sh + match: + kinds: + - apiGroups: [""] + kinds: ["FooIsBar"] + parameters: + foo: "bar" diff --git a/test/gator/verify/constraint_with_scopedEA_without_gator_ep.yaml b/test/gator/verify/constraint_with_scopedEA_without_gator_ep.yaml new file mode 100644 index 00000000000..9013748a1e9 --- /dev/null +++ b/test/gator/verify/constraint_with_scopedEA_without_gator_ep.yaml @@ -0,0 +1,16 @@ +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sFooIs +metadata: + name: foo-is-bar +spec: + enforcementAction: scoped + scopedEnforcementActions: + - action: warn + enforcementPoints: + - name: validation.gatekeeper.sh + match: + kinds: + - apiGroups: [""] + kinds: ["FooIsBar"] + parameters: + foo: "bar" diff --git a/test/gator/verify/suite.yaml b/test/gator/verify/suite.yaml index 6ec45b78873..1b769ccc193 100644 --- a/test/gator/verify/suite.yaml +++ b/test/gator/verify/suite.yaml @@ -13,3 +13,27 @@ tests: object: deny.yaml assertions: - violations: yes +- name: foo-is-bar-with-scopedEA + template: template.yaml + constraint: constraint_with_scopedEA.yaml + cases: + - name: foo-bar + object: allow.yaml + assertions: + - violations: no + - name: foo-not-bar + object: deny.yaml + assertions: + - violations: yes +- name: foo-is-bar-with-scopedEA-without-gator-EP + template: template.yaml + constraint: constraint_with_scopedEA_without_gator_ep.yaml + cases: + - name: foo-bar + object: allow.yaml + assertions: + - violations: no + - name: foo-not-bar + object: deny.yaml + assertions: + - violations: no \ No newline at end of file diff --git a/vendor/github.com/cespare/xxhash/v2/README.md b/vendor/github.com/cespare/xxhash/v2/README.md index 8bf0e5b7815..33c88305c46 100644 --- a/vendor/github.com/cespare/xxhash/v2/README.md +++ b/vendor/github.com/cespare/xxhash/v2/README.md @@ -70,3 +70,5 @@ benchstat <(go test -benchtime 500ms -count 15 -bench 'Sum64$') - [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics) - [FreeCache](https://github.com/coocood/freecache) - [FastCache](https://github.com/VictoriaMetrics/fastcache) +- [Ristretto](https://github.com/dgraph-io/ristretto) +- [Badger](https://github.com/dgraph-io/badger) diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash.go b/vendor/github.com/cespare/xxhash/v2/xxhash.go index a9e0d45c9dc..78bddf1ceed 100644 --- a/vendor/github.com/cespare/xxhash/v2/xxhash.go +++ b/vendor/github.com/cespare/xxhash/v2/xxhash.go @@ -19,10 +19,13 @@ const ( // Store the primes in an array as well. // // The consts are used when possible in Go code to avoid MOVs but we need a -// contiguous array of the assembly code. +// contiguous array for the assembly code. var primes = [...]uint64{prime1, prime2, prime3, prime4, prime5} // Digest implements hash.Hash64. +// +// Note that a zero-valued Digest is not ready to receive writes. +// Call Reset or create a Digest using New before calling other methods. type Digest struct { v1 uint64 v2 uint64 @@ -33,19 +36,31 @@ type Digest struct { n int // how much of mem is used } -// New creates a new Digest that computes the 64-bit xxHash algorithm. +// New creates a new Digest with a zero seed. func New() *Digest { + return NewWithSeed(0) +} + +// NewWithSeed creates a new Digest with the given seed. +func NewWithSeed(seed uint64) *Digest { var d Digest - d.Reset() + d.ResetWithSeed(seed) return &d } // Reset clears the Digest's state so that it can be reused. +// It uses a seed value of zero. func (d *Digest) Reset() { - d.v1 = primes[0] + prime2 - d.v2 = prime2 - d.v3 = 0 - d.v4 = -primes[0] + d.ResetWithSeed(0) +} + +// ResetWithSeed clears the Digest's state so that it can be reused. +// It uses the given seed to initialize the state. +func (d *Digest) ResetWithSeed(seed uint64) { + d.v1 = seed + prime1 + prime2 + d.v2 = seed + prime2 + d.v3 = seed + d.v4 = seed - prime1 d.total = 0 d.n = 0 } diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_asm.go b/vendor/github.com/cespare/xxhash/v2/xxhash_asm.go index 9216e0a40c1..78f95f25610 100644 --- a/vendor/github.com/cespare/xxhash/v2/xxhash_asm.go +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_asm.go @@ -6,7 +6,7 @@ package xxhash -// Sum64 computes the 64-bit xxHash digest of b. +// Sum64 computes the 64-bit xxHash digest of b with a zero seed. // //go:noescape func Sum64(b []byte) uint64 diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_other.go b/vendor/github.com/cespare/xxhash/v2/xxhash_other.go index 26df13bba4b..118e49e819e 100644 --- a/vendor/github.com/cespare/xxhash/v2/xxhash_other.go +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_other.go @@ -3,7 +3,7 @@ package xxhash -// Sum64 computes the 64-bit xxHash digest of b. +// Sum64 computes the 64-bit xxHash digest of b with a zero seed. func Sum64(b []byte) uint64 { // A simpler version would be // d := New() diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_safe.go b/vendor/github.com/cespare/xxhash/v2/xxhash_safe.go index e86f1b5fd8e..05f5e7dfe7b 100644 --- a/vendor/github.com/cespare/xxhash/v2/xxhash_safe.go +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_safe.go @@ -5,7 +5,7 @@ package xxhash -// Sum64String computes the 64-bit xxHash digest of s. +// Sum64String computes the 64-bit xxHash digest of s with a zero seed. func Sum64String(s string) uint64 { return Sum64([]byte(s)) } diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go b/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go index 1c1638fd88a..cf9d42aed53 100644 --- a/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go +++ b/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go @@ -33,7 +33,7 @@ import ( // // See https://github.com/golang/go/issues/42739 for discussion. -// Sum64String computes the 64-bit xxHash digest of s. +// Sum64String computes the 64-bit xxHash digest of s with a zero seed. // It may be faster than Sum64([]byte(s)) by avoiding a copy. func Sum64String(s string) uint64 { b := *(*[]byte)(unsafe.Pointer(&sliceHeader{s, len(s)})) diff --git a/vendor/github.com/containerd/containerd/archive/compression/compression.go b/vendor/github.com/containerd/containerd/archive/compression/compression.go index 31bbe412466..23ddfab1a6b 100644 --- a/vendor/github.com/containerd/containerd/archive/compression/compression.go +++ b/vendor/github.com/containerd/containerd/archive/compression/compression.go @@ -29,7 +29,7 @@ import ( "strconv" "sync" - "github.com/containerd/containerd/log" + "github.com/containerd/log" "github.com/klauspost/compress/zstd" ) diff --git a/vendor/github.com/containerd/containerd/content/helpers.go b/vendor/github.com/containerd/containerd/content/helpers.go index d3a82cb6f70..f4763847d39 100644 --- a/vendor/github.com/containerd/containerd/content/helpers.go +++ b/vendor/github.com/containerd/containerd/content/helpers.go @@ -24,9 +24,9 @@ import ( "sync" "time" - "github.com/containerd/containerd/errdefs" - "github.com/containerd/containerd/log" "github.com/containerd/containerd/pkg/randutil" + "github.com/containerd/errdefs" + "github.com/containerd/log" "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) diff --git a/vendor/github.com/containerd/containerd/content/local/locks.go b/vendor/github.com/containerd/containerd/content/local/locks.go index 1e59f39b30d..4caffcc02f4 100644 --- a/vendor/github.com/containerd/containerd/content/local/locks.go +++ b/vendor/github.com/containerd/containerd/content/local/locks.go @@ -21,7 +21,7 @@ import ( "sync" "time" - "github.com/containerd/containerd/errdefs" + "github.com/containerd/errdefs" ) // Handles locking references diff --git a/vendor/github.com/containerd/containerd/content/local/readerat.go b/vendor/github.com/containerd/containerd/content/local/readerat.go index 899e85c0ba8..7918844b19b 100644 --- a/vendor/github.com/containerd/containerd/content/local/readerat.go +++ b/vendor/github.com/containerd/containerd/content/local/readerat.go @@ -22,7 +22,7 @@ import ( "os" "github.com/containerd/containerd/content" - "github.com/containerd/containerd/errdefs" + "github.com/containerd/errdefs" ) // readerat implements io.ReaderAt in a completely stateless manner by opening diff --git a/vendor/github.com/containerd/containerd/content/local/store.go b/vendor/github.com/containerd/containerd/content/local/store.go index baae3565bb4..feecec79fc7 100644 --- a/vendor/github.com/containerd/containerd/content/local/store.go +++ b/vendor/github.com/containerd/containerd/content/local/store.go @@ -28,10 +28,10 @@ import ( "time" "github.com/containerd/containerd/content" - "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/filters" - "github.com/containerd/containerd/log" "github.com/containerd/containerd/pkg/randutil" + "github.com/containerd/errdefs" + "github.com/containerd/log" "github.com/sirupsen/logrus" "github.com/opencontainers/go-digest" diff --git a/vendor/github.com/containerd/containerd/content/local/writer.go b/vendor/github.com/containerd/containerd/content/local/writer.go index b187e524cb7..f82b131e167 100644 --- a/vendor/github.com/containerd/containerd/content/local/writer.go +++ b/vendor/github.com/containerd/containerd/content/local/writer.go @@ -27,8 +27,8 @@ import ( "time" "github.com/containerd/containerd/content" - "github.com/containerd/containerd/errdefs" - "github.com/containerd/containerd/log" + "github.com/containerd/errdefs" + "github.com/containerd/log" "github.com/opencontainers/go-digest" ) diff --git a/vendor/github.com/containerd/containerd/errdefs/errdefs_deprecated.go b/vendor/github.com/containerd/containerd/errdefs/errdefs_deprecated.go new file mode 100644 index 00000000000..c6a0d843ebe --- /dev/null +++ b/vendor/github.com/containerd/containerd/errdefs/errdefs_deprecated.go @@ -0,0 +1,116 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +// Package errdefs defines the common errors used throughout containerd +// packages. +// +// Use with fmt.Errorf to add context to an error. +// +// To detect an error class, use the IsXXX functions to tell whether an error +// is of a certain type. +// +// The functions ToGRPC and FromGRPC can be used to map server-side and +// client-side errors to the correct types. +package errdefs + +import ( + "github.com/containerd/errdefs" +) + +// Definitions of common error types used throughout containerd. All containerd +// errors returned by most packages will map into one of these errors classes. +// Packages should return errors of these types when they want to instruct a +// client to take a particular action. +// +// For the most part, we just try to provide local grpc errors. Most conditions +// map very well to those defined by grpc. +var ( + ErrUnknown = errdefs.ErrUnknown + ErrInvalidArgument = errdefs.ErrInvalidArgument + ErrNotFound = errdefs.ErrNotFound + ErrAlreadyExists = errdefs.ErrAlreadyExists + ErrFailedPrecondition = errdefs.ErrFailedPrecondition + ErrUnavailable = errdefs.ErrUnavailable + ErrNotImplemented = errdefs.ErrNotImplemented +) + +// IsInvalidArgument returns true if the error is due to an invalid argument +func IsInvalidArgument(err error) bool { + return errdefs.IsInvalidArgument(err) +} + +// IsNotFound returns true if the error is due to a missing object +func IsNotFound(err error) bool { + return errdefs.IsNotFound(err) +} + +// IsAlreadyExists returns true if the error is due to an already existing +// metadata item +func IsAlreadyExists(err error) bool { + return errdefs.IsAlreadyExists(err) +} + +// IsFailedPrecondition returns true if an operation could not proceed to the +// lack of a particular condition +func IsFailedPrecondition(err error) bool { + return errdefs.IsFailedPrecondition(err) +} + +// IsUnavailable returns true if the error is due to a resource being unavailable +func IsUnavailable(err error) bool { + return errdefs.IsUnavailable(err) +} + +// IsNotImplemented returns true if the error is due to not being implemented +func IsNotImplemented(err error) bool { + return errdefs.IsNotImplemented(err) +} + +// IsCanceled returns true if the error is due to `context.Canceled`. +func IsCanceled(err error) bool { + return errdefs.IsCanceled(err) +} + +// IsDeadlineExceeded returns true if the error is due to +// `context.DeadlineExceeded`. +func IsDeadlineExceeded(err error) bool { + return errdefs.IsDeadlineExceeded(err) +} + +// ToGRPC will attempt to map the backend containerd error into a grpc error, +// using the original error message as a description. +// +// Further information may be extracted from certain errors depending on their +// type. +// +// If the error is unmapped, the original error will be returned to be handled +// by the regular grpc error handling stack. +func ToGRPC(err error) error { + return errdefs.ToGRPC(err) +} + +// ToGRPCf maps the error to grpc error codes, assembling the formatting string +// and combining it with the target error string. +// +// This is equivalent to errdefs.ToGRPC(fmt.Errorf("%s: %w", fmt.Sprintf(format, args...), err)) +func ToGRPCf(err error, format string, args ...interface{}) error { + return errdefs.ToGRPCf(err, format, args...) +} + +// FromGRPC returns the underlying error from a grpc service based on the grpc error code +func FromGRPC(err error) error { + return errdefs.FromGRPC(err) +} diff --git a/vendor/github.com/containerd/containerd/filters/filter.go b/vendor/github.com/containerd/containerd/filters/filter.go index e13f2625c73..dcc569a4b7d 100644 --- a/vendor/github.com/containerd/containerd/filters/filter.go +++ b/vendor/github.com/containerd/containerd/filters/filter.go @@ -70,7 +70,7 @@ package filters import ( "regexp" - "github.com/containerd/containerd/log" + "github.com/containerd/log" ) // Filter matches specific resources based the provided filter diff --git a/vendor/github.com/containerd/containerd/filters/parser.go b/vendor/github.com/containerd/containerd/filters/parser.go index 32767909b1c..f07fd33bd2d 100644 --- a/vendor/github.com/containerd/containerd/filters/parser.go +++ b/vendor/github.com/containerd/containerd/filters/parser.go @@ -20,7 +20,7 @@ import ( "fmt" "io" - "github.com/containerd/containerd/errdefs" + "github.com/containerd/errdefs" ) /* diff --git a/vendor/github.com/containerd/containerd/images/diffid.go b/vendor/github.com/containerd/containerd/images/diffid.go index 56193cc289f..85577eedee4 100644 --- a/vendor/github.com/containerd/containerd/images/diffid.go +++ b/vendor/github.com/containerd/containerd/images/diffid.go @@ -36,7 +36,7 @@ func GetDiffID(ctx context.Context, cs content.Store, desc ocispec.Descriptor) ( MediaTypeDockerSchema2Layer, ocispec.MediaTypeImageLayer, MediaTypeDockerSchema2LayerForeign, - ocispec.MediaTypeImageLayerNonDistributable: + ocispec.MediaTypeImageLayerNonDistributable: //nolint:staticcheck // deprecated return desc.Digest, nil } info, err := cs.Info(ctx, desc.Digest) diff --git a/vendor/github.com/containerd/containerd/images/handlers.go b/vendor/github.com/containerd/containerd/images/handlers.go index 077d88e7877..162e87a862d 100644 --- a/vendor/github.com/containerd/containerd/images/handlers.go +++ b/vendor/github.com/containerd/containerd/images/handlers.go @@ -23,8 +23,8 @@ import ( "sort" "github.com/containerd/containerd/content" - "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/platforms" + "github.com/containerd/errdefs" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "golang.org/x/sync/errgroup" "golang.org/x/sync/semaphore" diff --git a/vendor/github.com/containerd/containerd/images/image.go b/vendor/github.com/containerd/containerd/images/image.go index 2d2e36a9a30..3e2abc75f43 100644 --- a/vendor/github.com/containerd/containerd/images/image.go +++ b/vendor/github.com/containerd/containerd/images/image.go @@ -24,9 +24,9 @@ import ( "time" "github.com/containerd/containerd/content" - "github.com/containerd/containerd/errdefs" - "github.com/containerd/containerd/log" "github.com/containerd/containerd/platforms" + "github.com/containerd/errdefs" + "github.com/containerd/log" digest "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -268,6 +268,9 @@ func Platforms(ctx context.Context, provider content.Provider, image ocispec.Des var platformSpecs []ocispec.Platform return platformSpecs, Walk(ctx, Handlers(HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { if desc.Platform != nil { + if desc.Platform.OS == "unknown" || desc.Platform.Architecture == "unknown" { + return nil, ErrSkipDesc + } platformSpecs = append(platformSpecs, *desc.Platform) return nil, ErrSkipDesc } diff --git a/vendor/github.com/containerd/containerd/images/mediatypes.go b/vendor/github.com/containerd/containerd/images/mediatypes.go index 067963babba..cd51aa5ebbd 100644 --- a/vendor/github.com/containerd/containerd/images/mediatypes.go +++ b/vendor/github.com/containerd/containerd/images/mediatypes.go @@ -22,7 +22,7 @@ import ( "sort" "strings" - "github.com/containerd/containerd/errdefs" + "github.com/containerd/errdefs" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -81,7 +81,7 @@ func DiffCompression(ctx context.Context, mediaType string) (string, error) { return "", nil } return "gzip", nil - case ocispec.MediaTypeImageLayer, ocispec.MediaTypeImageLayerNonDistributable: + case ocispec.MediaTypeImageLayer, ocispec.MediaTypeImageLayerNonDistributable: //nolint:staticcheck // Non-distributable layers are deprecated if len(ext) > 0 { switch ext[len(ext)-1] { case "gzip": diff --git a/vendor/github.com/containerd/containerd/labels/validate.go b/vendor/github.com/containerd/containerd/labels/validate.go index f83b5dde294..6f23cdd7c65 100644 --- a/vendor/github.com/containerd/containerd/labels/validate.go +++ b/vendor/github.com/containerd/containerd/labels/validate.go @@ -19,7 +19,7 @@ package labels import ( "fmt" - "github.com/containerd/containerd/errdefs" + "github.com/containerd/errdefs" ) const ( diff --git a/vendor/github.com/containerd/containerd/platforms/cpuinfo.go b/vendor/github.com/containerd/containerd/platforms/cpuinfo.go index 8c600fc96b1..91f50e8c88a 100644 --- a/vendor/github.com/containerd/containerd/platforms/cpuinfo.go +++ b/vendor/github.com/containerd/containerd/platforms/cpuinfo.go @@ -20,7 +20,7 @@ import ( "runtime" "sync" - "github.com/containerd/containerd/log" + "github.com/containerd/log" ) // Present the ARM instruction set architecture, eg: v7, v8 diff --git a/vendor/github.com/containerd/containerd/platforms/cpuinfo_linux.go b/vendor/github.com/containerd/containerd/platforms/cpuinfo_linux.go index 722d86c3578..e07aa99cc15 100644 --- a/vendor/github.com/containerd/containerd/platforms/cpuinfo_linux.go +++ b/vendor/github.com/containerd/containerd/platforms/cpuinfo_linux.go @@ -24,7 +24,7 @@ import ( "runtime" "strings" - "github.com/containerd/containerd/errdefs" + "github.com/containerd/errdefs" "golang.org/x/sys/unix" ) diff --git a/vendor/github.com/containerd/containerd/platforms/cpuinfo_other.go b/vendor/github.com/containerd/containerd/platforms/cpuinfo_other.go index fa5f19c427a..8cbcbb24afb 100644 --- a/vendor/github.com/containerd/containerd/platforms/cpuinfo_other.go +++ b/vendor/github.com/containerd/containerd/platforms/cpuinfo_other.go @@ -22,7 +22,7 @@ import ( "fmt" "runtime" - "github.com/containerd/containerd/errdefs" + "github.com/containerd/errdefs" ) func getCPUVariant() (string, error) { diff --git a/vendor/github.com/containerd/containerd/platforms/platforms.go b/vendor/github.com/containerd/containerd/platforms/platforms.go index 56613b07656..44bc24a5c67 100644 --- a/vendor/github.com/containerd/containerd/platforms/platforms.go +++ b/vendor/github.com/containerd/containerd/platforms/platforms.go @@ -116,7 +116,7 @@ import ( specs "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/containerd/containerd/errdefs" + "github.com/containerd/errdefs" ) var ( diff --git a/vendor/github.com/containerd/containerd/remotes/docker/auth/fetch.go b/vendor/github.com/containerd/containerd/remotes/docker/auth/fetch.go index 64c6a38f91a..244e03509a7 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/auth/fetch.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/auth/fetch.go @@ -26,9 +26,9 @@ import ( "strings" "time" - "github.com/containerd/containerd/log" remoteserrors "github.com/containerd/containerd/remotes/errors" "github.com/containerd/containerd/version" + "github.com/containerd/log" ) var ( diff --git a/vendor/github.com/containerd/containerd/remotes/docker/authorizer.go b/vendor/github.com/containerd/containerd/remotes/docker/authorizer.go index 9b3663cd141..2fd1118bc77 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/authorizer.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/authorizer.go @@ -25,10 +25,10 @@ import ( "strings" "sync" - "github.com/containerd/containerd/errdefs" - "github.com/containerd/containerd/log" "github.com/containerd/containerd/remotes/docker/auth" remoteerrors "github.com/containerd/containerd/remotes/errors" + "github.com/containerd/errdefs" + "github.com/containerd/log" ) type dockerAuthorizer struct { @@ -148,9 +148,11 @@ func (a *dockerAuthorizer) AddResponses(ctx context.Context, responses []*http.R defer a.mu.Unlock() for _, c := range auth.ParseAuthHeader(last.Header) { if c.Scheme == auth.BearerAuth { - if err := invalidAuthorization(c, responses); err != nil { + if retry, err := invalidAuthorization(ctx, c, responses); err != nil { delete(a.handlers, host) return err + } else if retry { + delete(a.handlers, host) } // reuse existing handler @@ -328,18 +330,24 @@ func (ah *authHandler) doBearerAuth(ctx context.Context) (token, refreshToken st return resp.Token, resp.RefreshToken, nil } -func invalidAuthorization(c auth.Challenge, responses []*http.Response) error { +func invalidAuthorization(ctx context.Context, c auth.Challenge, responses []*http.Response) (retry bool, _ error) { errStr := c.Parameters["error"] if errStr == "" { - return nil + return retry, nil } n := len(responses) if n == 1 || (n > 1 && !sameRequest(responses[n-2].Request, responses[n-1].Request)) { - return nil + limitedErr := errStr + errLenghLimit := 64 + if len(limitedErr) > errLenghLimit { + limitedErr = limitedErr[:errLenghLimit] + "..." + } + log.G(ctx).WithField("error", limitedErr).Debug("authorization error using bearer token, retrying") + return true, nil } - return fmt.Errorf("server message: %s: %w", errStr, ErrInvalidAuthorization) + return retry, fmt.Errorf("server message: %s: %w", errStr, ErrInvalidAuthorization) } func sameRequest(r1, r2 *http.Request) bool { diff --git a/vendor/github.com/containerd/containerd/remotes/docker/converter.go b/vendor/github.com/containerd/containerd/remotes/docker/converter.go index d7dca0d3643..95a68d70e67 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/converter.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/converter.go @@ -24,8 +24,8 @@ import ( "github.com/containerd/containerd/content" "github.com/containerd/containerd/images" - "github.com/containerd/containerd/log" "github.com/containerd/containerd/remotes" + "github.com/containerd/log" digest "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) diff --git a/vendor/github.com/containerd/containerd/remotes/docker/converter_fuzz.go b/vendor/github.com/containerd/containerd/remotes/docker/converter_fuzz.go index 9082053924b..aa7cf4666f7 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/converter_fuzz.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/converter_fuzz.go @@ -24,7 +24,7 @@ import ( fuzz "github.com/AdaLogics/go-fuzz-headers" "github.com/containerd/containerd/content/local" - "github.com/containerd/containerd/log" + "github.com/containerd/log" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" ) diff --git a/vendor/github.com/containerd/containerd/remotes/docker/fetcher.go b/vendor/github.com/containerd/containerd/remotes/docker/fetcher.go index ecf245933f7..3589db3ef91 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/fetcher.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/fetcher.go @@ -26,9 +26,9 @@ import ( "net/url" "strings" - "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" - "github.com/containerd/containerd/log" + "github.com/containerd/errdefs" + "github.com/containerd/log" digest "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) diff --git a/vendor/github.com/containerd/containerd/remotes/docker/handler.go b/vendor/github.com/containerd/containerd/remotes/docker/handler.go index 27638ccc02f..ccec490133b 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/handler.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/handler.go @@ -25,8 +25,8 @@ import ( "github.com/containerd/containerd/content" "github.com/containerd/containerd/images" "github.com/containerd/containerd/labels" - "github.com/containerd/containerd/log" "github.com/containerd/containerd/reference" + "github.com/containerd/log" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) diff --git a/vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go b/vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go index 82435933906..6739e7904e1 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go @@ -21,8 +21,8 @@ import ( "fmt" "io" - "github.com/containerd/containerd/errdefs" - "github.com/containerd/containerd/log" + "github.com/containerd/errdefs" + "github.com/containerd/log" ) const maxRetry = 3 diff --git a/vendor/github.com/containerd/containerd/remotes/docker/pusher.go b/vendor/github.com/containerd/containerd/remotes/docker/pusher.go index 218a5dd30f2..a27cda0b56c 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/pusher.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/pusher.go @@ -29,11 +29,11 @@ import ( "time" "github.com/containerd/containerd/content" - "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" - "github.com/containerd/containerd/log" "github.com/containerd/containerd/remotes" remoteserrors "github.com/containerd/containerd/remotes/errors" + "github.com/containerd/errdefs" + "github.com/containerd/log" digest "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) diff --git a/vendor/github.com/containerd/containerd/remotes/docker/resolver.go b/vendor/github.com/containerd/containerd/remotes/docker/resolver.go index cca4ca6a237..b2b1242140b 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/resolver.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/resolver.go @@ -28,15 +28,15 @@ import ( "path" "strings" - "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" - "github.com/containerd/containerd/log" "github.com/containerd/containerd/reference" "github.com/containerd/containerd/remotes" "github.com/containerd/containerd/remotes/docker/schema1" //nolint:staticcheck // Ignore SA1019. Need to keep deprecated package for compatibility. remoteerrors "github.com/containerd/containerd/remotes/errors" "github.com/containerd/containerd/tracing" "github.com/containerd/containerd/version" + "github.com/containerd/errdefs" + "github.com/containerd/log" "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -704,9 +704,71 @@ func IsLocalhost(host string) bool { return ip.IsLoopback() } +// NewHTTPFallback returns http.RoundTripper which allows fallback from https to +// http for registry endpoints with configurations for both http and TLS, +// such as defaulted localhost endpoints. +func NewHTTPFallback(transport http.RoundTripper) http.RoundTripper { + return &httpFallback{ + super: transport, + } +} + +type httpFallback struct { + super http.RoundTripper + host string +} + +func (f *httpFallback) RoundTrip(r *http.Request) (*http.Response, error) { + // only fall back if the same host had previously fell back + if f.host != r.URL.Host { + resp, err := f.super.RoundTrip(r) + if !isTLSError(err) { + return resp, err + } + } + + plainHTTPUrl := *r.URL + plainHTTPUrl.Scheme = "http" + + plainHTTPRequest := *r + plainHTTPRequest.URL = &plainHTTPUrl + + if f.host != r.URL.Host { + f.host = r.URL.Host + + // update body on the second attempt + if r.Body != nil && r.GetBody != nil { + body, err := r.GetBody() + if err != nil { + return nil, err + } + plainHTTPRequest.Body = body + } + } + + return f.super.RoundTrip(&plainHTTPRequest) +} + +func isTLSError(err error) bool { + if err == nil { + return false + } + var tlsErr tls.RecordHeaderError + if errors.As(err, &tlsErr) && string(tlsErr.RecordHeader[:]) == "HTTP/" { + return true + } + if strings.Contains(err.Error(), "TLS handshake timeout") { + return true + } + + return false +} + // HTTPFallback is an http.RoundTripper which allows fallback from https to http // for registry endpoints with configurations for both http and TLS, such as // defaulted localhost endpoints. +// +// Deprecated: Use NewHTTPFallback instead. type HTTPFallback struct { http.RoundTripper } @@ -722,6 +784,14 @@ func (f HTTPFallback) RoundTrip(r *http.Request) (*http.Response, error) { plainHTTPRequest := *r plainHTTPRequest.URL = &plainHTTPUrl + if r.Body != nil && r.GetBody != nil { + body, err := r.GetBody() + if err != nil { + return nil, err + } + plainHTTPRequest.Body = body + } + return f.RoundTripper.RoundTrip(&plainHTTPRequest) } diff --git a/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go b/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go index 8c9e520cd27..75bd9875a4c 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go @@ -34,11 +34,11 @@ import ( "github.com/containerd/containerd/archive/compression" "github.com/containerd/containerd/content" - "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" "github.com/containerd/containerd/labels" - "github.com/containerd/containerd/log" "github.com/containerd/containerd/remotes" + "github.com/containerd/errdefs" + "github.com/containerd/log" digest "github.com/opencontainers/go-digest" specs "github.com/opencontainers/image-spec/specs-go" ocispec "github.com/opencontainers/image-spec/specs-go/v1" diff --git a/vendor/github.com/containerd/containerd/remotes/docker/status.go b/vendor/github.com/containerd/containerd/remotes/docker/status.go index 1a9227725bb..c7764758f0e 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/status.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/status.go @@ -21,7 +21,7 @@ import ( "sync" "github.com/containerd/containerd/content" - "github.com/containerd/containerd/errdefs" + "github.com/containerd/errdefs" "github.com/moby/locker" ) diff --git a/vendor/github.com/containerd/containerd/remotes/handlers.go b/vendor/github.com/containerd/containerd/remotes/handlers.go index f24669dc4ac..912b85bfe80 100644 --- a/vendor/github.com/containerd/containerd/remotes/handlers.go +++ b/vendor/github.com/containerd/containerd/remotes/handlers.go @@ -26,11 +26,11 @@ import ( "sync" "github.com/containerd/containerd/content" - "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" "github.com/containerd/containerd/labels" - "github.com/containerd/containerd/log" "github.com/containerd/containerd/platforms" + "github.com/containerd/errdefs" + "github.com/containerd/log" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "golang.org/x/sync/semaphore" ) diff --git a/vendor/github.com/containerd/containerd/version/version.go b/vendor/github.com/containerd/containerd/version/version.go index 4c4c67143a8..dfe46c922f0 100644 --- a/vendor/github.com/containerd/containerd/version/version.go +++ b/vendor/github.com/containerd/containerd/version/version.go @@ -23,7 +23,7 @@ var ( Package = "github.com/containerd/containerd" // Version holds the complete version number. Filled in at linking time. - Version = "1.7.15+unknown" + Version = "1.7.18+unknown" // Revision is filled with the VCS (e.g. git) revision being used to build // the program at linking time. diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/LICENSE b/vendor/github.com/containerd/errdefs/LICENSE similarity index 93% rename from vendor/github.com/open-policy-agent/frameworks/constraint/LICENSE rename to vendor/github.com/containerd/errdefs/LICENSE index 8f71f43fee3..584149b6ee2 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/LICENSE +++ b/vendor/github.com/containerd/errdefs/LICENSE @@ -1,6 +1,7 @@ + Apache License Version 2.0, January 2004 - http://www.apache.org/licenses/ + https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -175,28 +176,16 @@ END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} + Copyright The containerd Authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - diff --git a/vendor/github.com/containerd/errdefs/README.md b/vendor/github.com/containerd/errdefs/README.md new file mode 100644 index 00000000000..bd418c63f98 --- /dev/null +++ b/vendor/github.com/containerd/errdefs/README.md @@ -0,0 +1,13 @@ +# errdefs + +A Go package for defining and checking common containerd errors. + +## Project details + +**errdefs** is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE). +As a containerd sub-project, you will find the: + * [Project governance](https://github.com/containerd/project/blob/main/GOVERNANCE.md), + * [Maintainers](https://github.com/containerd/project/blob/main/MAINTAINERS), + * and [Contributing guidelines](https://github.com/containerd/project/blob/main/CONTRIBUTING.md) + +information in our [`containerd/project`](https://github.com/containerd/project) repository. diff --git a/vendor/github.com/containerd/containerd/errdefs/errors.go b/vendor/github.com/containerd/errdefs/errors.go similarity index 100% rename from vendor/github.com/containerd/containerd/errdefs/errors.go rename to vendor/github.com/containerd/errdefs/errors.go diff --git a/vendor/github.com/containerd/containerd/errdefs/grpc.go b/vendor/github.com/containerd/errdefs/grpc.go similarity index 100% rename from vendor/github.com/containerd/containerd/errdefs/grpc.go rename to vendor/github.com/containerd/errdefs/grpc.go diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/deploy/crds.yaml b/vendor/github.com/open-policy-agent/frameworks/constraint/deploy/crds.yaml index 61e8feef0de..83551848f54 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/deploy/crds.yaml +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/deploy/crds.yaml @@ -17,8 +17,7 @@ spec: - name: v1 schema: openAPIV3Schema: - description: ConstraintTemplate is the Schema for the constrainttemplates - API + description: ConstraintTemplate is the Schema for the constrainttemplates API properties: apiVersion: description: |- @@ -76,8 +75,7 @@ spec: items: properties: engine: - description: 'The engine used to evaluate the code. Example: - "Rego". Required.' + description: 'The engine used to evaluate the code. Example: "Rego". Required.' type: string source: description: The source code for the template. Required. @@ -112,8 +110,7 @@ spec: properties: errors: items: - description: CreateCRDError represents a single error caught - during parsing, compiling, etc. + description: CreateCRDError represents a single error caught during parsing, compiling, etc. properties: code: type: string @@ -127,8 +124,7 @@ spec: type: object type: array id: - description: a unique identifier for the pod that wrote the - status + description: a unique identifier for the pod that wrote the status type: string observedGeneration: format: int64 @@ -147,8 +143,7 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: ConstraintTemplate is the Schema for the constrainttemplates - API + description: ConstraintTemplate is the Schema for the constrainttemplates API properties: apiVersion: description: |- @@ -206,8 +201,7 @@ spec: items: properties: engine: - description: 'The engine used to evaluate the code. Example: - "Rego". Required.' + description: 'The engine used to evaluate the code. Example: "Rego". Required.' type: string source: description: The source code for the template. Required. @@ -242,8 +236,7 @@ spec: properties: errors: items: - description: CreateCRDError represents a single error caught - during parsing, compiling, etc. + description: CreateCRDError represents a single error caught during parsing, compiling, etc. properties: code: type: string @@ -257,8 +250,7 @@ spec: type: object type: array id: - description: a unique identifier for the pod that wrote the - status + description: a unique identifier for the pod that wrote the status type: string observedGeneration: format: int64 @@ -277,8 +269,7 @@ spec: - name: v1beta1 schema: openAPIV3Schema: - description: ConstraintTemplate is the Schema for the constrainttemplates - API + description: ConstraintTemplate is the Schema for the constrainttemplates API properties: apiVersion: description: |- @@ -336,8 +327,7 @@ spec: items: properties: engine: - description: 'The engine used to evaluate the code. Example: - "Rego". Required.' + description: 'The engine used to evaluate the code. Example: "Rego". Required.' type: string source: description: The source code for the template. Required. @@ -372,8 +362,7 @@ spec: properties: errors: items: - description: CreateCRDError represents a single error caught - during parsing, compiling, etc. + description: CreateCRDError represents a single error caught during parsing, compiling, etc. properties: code: type: string @@ -387,8 +376,7 @@ spec: type: object type: array id: - description: a unique identifier for the pod that wrote the - status + description: a unique identifier for the pod that wrote the status type: string observedGeneration: format: int64 @@ -421,8 +409,7 @@ spec: scope: Cluster versions: - deprecated: true - deprecationWarning: externaldata.gatekeeper.sh/v1alpha1 is deprecated. Use externaldata.gatekeeper.sh/v1beta1 - instead. + deprecationWarning: externaldata.gatekeeper.sh/v1alpha1 is deprecated. Use externaldata.gatekeeper.sh/v1beta1 instead. name: v1alpha1 schema: openAPIV3Schema: @@ -457,8 +444,7 @@ spec: description: Timeout is the timeout when querying the provider. type: integer url: - description: URL is the url for the provider. URL is prefixed with - https://. + description: URL is the url for the provider. URL is prefixed with https://. type: string type: object type: object @@ -498,8 +484,7 @@ spec: description: Timeout is the timeout when querying the provider. type: integer url: - description: URL is the url for the provider. URL is prefixed with - https://. + description: URL is the url for the provider. URL is prefixed with https://. type: string type: object type: object diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/deploy/tools.go b/vendor/github.com/open-policy-agent/frameworks/constraint/deploy/tools.go index 71938e109a7..a3201a22eec 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/deploy/tools.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/deploy/tools.go @@ -1,3 +1,4 @@ +//go:build tools // +build tools // This existence of this package allows vendoring of the manifests in this directory by go 1.13+. diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go index 4dbb15318cb..a64e7a3d299 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go @@ -3,10 +3,21 @@ package constraints import ( "errors" "fmt" + "strings" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" ) +type ScopedEnforcementAction struct { + Action string `json:"action"` + EnforcementPoints []EnforcementPoint `json:"enforcementPoints"` +} + +type EnforcementPoint struct { + Name string `json:"name"` +} + const ( // Group is the API Group of Constraints. Group = "constraints.gatekeeper.sh" @@ -17,6 +28,11 @@ const ( // // This is the default EnforcementAction. EnforcementActionDeny = "deny" + + EnforcementActionScoped = "scoped" + + // AllEnforcementPoints is a wildcard to indicate all enforcement points. + AllEnforcementPoints = "*" ) var ( @@ -26,6 +42,9 @@ var ( // ErrSchema is a specific error that a Constraint failed schema validation. ErrSchema = errors.New("schema validation failed") + + // ErrMissingRequiredField is a specific error that a field is missing from a Constraint. + ErrMissingRequiredField = errors.New("missing required field") ) // GetEnforcementAction returns a Constraint's enforcementAction, which indicates @@ -45,3 +64,101 @@ func GetEnforcementAction(constraint *unstructured.Unstructured) (string, error) return action, nil } + +func IsEnforcementActionScoped(action string) bool { + return strings.EqualFold(action, EnforcementActionScoped) +} + +// GetEnforcementActionsForEP returns a map of enforcement actions for enforcement points passed in. +func GetEnforcementActionsForEP(constraint *unstructured.Unstructured, eps []string) (map[string]map[string]bool, error) { + if len(eps) == 0 { + return nil, fmt.Errorf("enforcement points must be provided to get enforcement actions") + } + + scopedActions, found, err := getNestedFieldAsArray(constraint.Object, "spec", "scopedEnforcementActions") + if err != nil { + return nil, fmt.Errorf("%w: invalid spec.enforcementActionPerEP", ErrInvalidConstraint) + } + if !found { + return nil, fmt.Errorf("%w: spec.scopedEnforcementAction must be defined", ErrMissingRequiredField) + } + + scopedEnforcementActions, err := convertToSliceScopedEnforcementAction(scopedActions) + if err != nil { + return nil, fmt.Errorf("%w: %w", ErrInvalidConstraint, err) + } + + // Flag to indicate if all enforcement points should be enforced + enforceAll := false + // Initialize a map to hold enforcement actions for each enforcement point + actionsForEPs := make(map[string]map[string]bool) + // Populate the actionsForEPs map with enforcement points from eps, initializing their action maps + for _, enforcementPoint := range eps { + if enforcementPoint == AllEnforcementPoints { + enforceAll = true // Set enforceAll to true if the special identifier for all enforcement points is found + } + actionsForEPs[enforcementPoint] = make(map[string]bool) // Initialize the action map for the enforcement point + } + + // Iterate over the scoped enforcement actions to populate actions for each enforcement point + for _, scopedEA := range scopedEnforcementActions { + for _, enforcementPoint := range scopedEA.EnforcementPoints { + epName := strings.ToLower(enforcementPoint.Name) + ea := strings.ToLower(scopedEA.Action) + // If enforceAll is true, or the enforcement point is explicitly listed, initialize its action map + if _, ok := actionsForEPs[epName]; !ok && enforceAll { + actionsForEPs[epName] = make(map[string]bool) + } + // Skip adding actions for enforcement points not in the list unless enforceAll is true + if _, ok := actionsForEPs[epName]; !ok && epName != AllEnforcementPoints { + continue + } + // If the enforcement point is the special identifier for all, apply the action to all enforcement points + switch epName { + case AllEnforcementPoints: + for ep := range actionsForEPs { + actionsForEPs[ep][ea] = true + } + default: + actionsForEPs[epName][ea] = true + } + } + } + + return actionsForEPs, nil +} + +// Helper function to access nested fields as an array. +func getNestedFieldAsArray(obj map[string]interface{}, fields ...string) ([]interface{}, bool, error) { + value, found, err := unstructured.NestedFieldNoCopy(obj, fields...) + if err != nil { + return nil, false, err + } + if !found { + return nil, false, nil + } + if arr, ok := value.([]interface{}); ok { + return arr, true, nil + } + return nil, false, nil +} + +// Helper function to convert a value to a []ScopedEnforcementAction. +func convertToSliceScopedEnforcementAction(value interface{}) ([]ScopedEnforcementAction, error) { + var result []ScopedEnforcementAction + if arr, ok := value.([]interface{}); ok { + for _, v := range arr { + if m, ok := v.(map[string]interface{}); ok { + scopedEA := &ScopedEnforcementAction{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(m, scopedEA); err != nil { + return nil, err + } + result = append(result, *scopedEA) + } else { + return nil, fmt.Errorf("scopedEnforcementActions value must be a []scopedEnforcementAction{action: string, enforcementPoints: []EnforcementPoint{name: string}}") + } + } + return result, nil + } + return nil, fmt.Errorf("scopedEnforcementActions value must be a []scopedEnforcementAction{action: string, enforcementPoints: []EnforcementPoint{name: string}}") +} diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client.go index 5a2a1468c2c..563734d1442 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client.go @@ -14,6 +14,7 @@ import ( regoSchema "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/rego/schema" "github.com/open-policy-agent/frameworks/constraint/pkg/client/errors" clienterrors "github.com/open-policy-agent/frameworks/constraint/pkg/client/errors" + "github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews" "github.com/open-policy-agent/frameworks/constraint/pkg/core/templates" "github.com/open-policy-agent/frameworks/constraint/pkg/handler" "github.com/open-policy-agent/frameworks/constraint/pkg/instrumentation" @@ -59,6 +60,9 @@ type Client struct { // templates is a map from a Template's name to its entry. templates map[string]*templateClient + + // enforcementPoints is array of enforcement points for which this client may be used. + enforcementPoints []string } // driverForTemplate returns the driver to be used for a template according @@ -363,7 +367,7 @@ func (c *Client) AddConstraint(ctx context.Context, constraint *unstructured.Uns return resp, err } - changed, err := cached.AddConstraint(constraintWithDefaults) + changed, err := cached.AddConstraint(constraintWithDefaults, c.enforcementPoints) if err != nil { return resp, err } @@ -619,10 +623,55 @@ func (c *Client) RemoveData(ctx context.Context, data interface{}) (*types.Respo return resp, &errMap } -// Review makes sure the provided object satisfies all stored constraints. +// Review makes sure the provided object satisfies constraints applicable for specific enforcement points. // On error, the responses return value will still be populated so that // partial results can be analyzed. -func (c *Client) Review(ctx context.Context, obj interface{}, opts ...drivers.QueryOpt) (*types.Responses, error) { +func (c *Client) Review(ctx context.Context, obj interface{}, opts ...reviews.ReviewOpt) (*types.Responses, error) { + // Initialize an empty slice for enforcement points + var eps []string + // Create a new ReviewCfg instance + cfg := &reviews.ReviewCfg{} + // Apply each option to the ReviewCfg instance + for _, opt := range opts { + opt(cfg) + } + + // Check if a source enforcement point is specified in the configuration + if cfg.SourceEP != "" { + // Initialize enforceAll as false. It will be used to determine if all enforcement points should be enforced + enforceAll := false + // Iterate through the client's enforcement points + for _, ep := range c.enforcementPoints { + // Check if the current enforcement point indicates all enforcement points should be enforced + if ep == apiconstraints.AllEnforcementPoints { + enforceAll = true + } + // If the specified source enforcement point matches the current enforcement point, add it to the list + if cfg.SourceEP == ep { + eps = append(eps, ep) + break // Exit the loop since the matching enforcement point is found + } + } + // If enforceAll is true, add the source enforcement point to the list of enforcement points + if enforceAll { + eps = append(eps, cfg.SourceEP) + } + // If no enforcement points match the source enforcement point, return nil indicating no review should be run + if eps == nil { + return nil, nil + } + } + + // If no specific enforcement points are specified, use the client's enforcement points + if eps == nil { + eps = c.enforcementPoints + } + + // If there are no enforcement points specified, default to using all enforcement points + if eps == nil { + eps = []string{apiconstraints.AllEnforcementPoints} + } + responses := types.NewResponses() errMap := make(clienterrors.ErrorMap) @@ -647,6 +696,8 @@ func (c *Client) Review(ctx context.Context, obj interface{}, opts ...drivers.Qu constraintsByTarget := make(map[string][]*unstructured.Unstructured) autorejections := make(map[string][]constraintMatchResult) + scopedEnforcementActionsByTarget := make(map[string]map[string][]string) + enforcementActionByTarget := make(map[string]map[string]string) var templateList []*templateClient @@ -659,18 +710,23 @@ func (c *Client) Review(ctx context.Context, obj interface{}, opts ...drivers.Qu for target, review := range reviews { var targetConstraints []*unstructured.Unstructured - + targetScopedEnforcementActions := make(map[string][]string) + targetEnforcementAction := make(map[string]string) for _, template := range templateList { - matchingConstraints := template.Matches(target, review) + matchingConstraints := template.Matches(target, review, eps) for _, matchResult := range matchingConstraints { if matchResult.error == nil { targetConstraints = append(targetConstraints, matchResult.constraint) + targetScopedEnforcementActions[matchResult.constraint.GetName()] = matchResult.scopedEnforcementActions + targetEnforcementAction[matchResult.constraint.GetName()] = matchResult.enforcementAction } else { autorejections[target] = append(autorejections[target], matchResult) } } } constraintsByTarget[target] = targetConstraints + scopedEnforcementActionsByTarget[target] = targetScopedEnforcementActions + enforcementActionByTarget[target] = targetEnforcementAction } for target, review := range reviews { @@ -682,6 +738,15 @@ func (c *Client) Review(ctx context.Context, obj interface{}, opts ...drivers.Qu continue } + for i := range resp.Results { + if val, ok := scopedEnforcementActionsByTarget[target][resp.Results[i].Constraint.GetName()]; ok { + resp.Results[i].ScopedEnforcementActions = val + } + if val, ok := enforcementActionByTarget[target][resp.Results[i].Constraint.GetName()]; ok { + resp.Results[i].EnforcementAction = val + } + } + for _, autorejection := range autorejections[target] { resp.AddResult(autorejection.ToResult()) } @@ -711,7 +776,7 @@ func (c *Client) Review(ctx context.Context, obj interface{}, opts ...drivers.Qu return responses, &errMap } -func (c *Client) review(ctx context.Context, target string, constraints []*unstructured.Unstructured, review interface{}, opts ...drivers.QueryOpt) (*types.Response, []*instrumentation.StatsEntry, error) { +func (c *Client) review(ctx context.Context, target string, constraints []*unstructured.Unstructured, review interface{}, opts ...reviews.ReviewOpt) (*types.Response, []*instrumentation.StatsEntry, error) { var results []*types.Result var stats []*instrumentation.StatsEntry var tracesBuilder strings.Builder diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client_opts.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client_opts.go index 0a70fb9b59a..775b2d9aedd 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client_opts.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client_opts.go @@ -4,6 +4,7 @@ import ( "fmt" "regexp" "sort" + "strings" "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers" "github.com/open-policy-agent/frameworks/constraint/pkg/handler" @@ -72,3 +73,13 @@ func IgnoreNoReferentialDriverWarning(ignore bool) Opt { return nil } } + +func EnforcementPoints(eps ...string) Opt { + return func(client *Client) error { + for i, ep := range eps { + eps[i] = strings.ToLower(ep) + } + client.enforcementPoints = eps + return nil + } +} diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/constraint_client.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/constraint_client.go index dd02b9ea1f0..0d21481a012 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/constraint_client.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/constraint_client.go @@ -3,6 +3,7 @@ package client import ( "fmt" + apiconstraints "github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints" "github.com/open-policy-agent/frameworks/constraint/pkg/client/errors" "github.com/open-policy-agent/frameworks/constraint/pkg/core/constraints" "github.com/open-policy-agent/frameworks/constraint/pkg/types" @@ -16,24 +17,58 @@ type constraintClient struct { // constraint is a copy of the original Constraint added to Client. constraint *unstructured.Unstructured + // matchers are the per-target Matchers for this Constraint. + matchers map[string]constraints.Matcher + // enforcementAction is what should be done if the Constraint is violated or // fails to run on a review. enforcementAction string - // matchers are the per-target Matchers for this Constraint. - matchers map[string]constraints.Matcher + // enforcementActionsForEP stores precompiled enforcement actions for each enforcement point. + enforcementActionsForEP map[string][]string } func (c *constraintClient) getConstraint() *unstructured.Unstructured { return c.constraint.DeepCopy() } -func (c *constraintClient) matches(target string, review interface{}) *constraintMatchResult { +func (c *constraintClient) matches(target string, review interface{}, sourceEPs ...string) *constraintMatchResult { matcher, found := c.matchers[target] if !found { return nil } + enforcementActions := make(map[string]bool) + if apiconstraints.IsEnforcementActionScoped(c.enforcementAction) { + // Initialize a map to track unique enforcement actions + // Iterate over the provided source enforcement points (EPs) + for _, ep := range sourceEPs { + var actions []string + // Check if there are predefined actions for the current EP + if acts, found := c.enforcementActionsForEP[ep]; found { + actions = acts // Use the predefined actions if found + } else if ep == "*" { + // If the EP is "*", aggregate actions from all EPs + for _, acts := range c.enforcementActionsForEP { + actions = append(actions, acts...) + } + } + // Mark each action as true in the map to ensure uniqueness + for _, act := range actions { + enforcementActions[act] = true + } + } + + } + // If no enforcement actions are found, return nil + if len(enforcementActions) == 0 && apiconstraints.IsEnforcementActionScoped(c.enforcementAction) { + return nil + } + + var actions []string + for action := range enforcementActions { + actions = append(actions, action) + } matches, err := matcher.Match(review) // We avoid DeepCopying the Constraint out of the Client cache here, only @@ -47,14 +82,17 @@ func (c *constraintClient) matches(target string, review interface{}) *constrain // determine if the Constraint matched, so we assume it violated the // Constraint. return &constraintMatchResult{ - constraint: c.constraint, - error: fmt.Errorf("%w: %v", errors.ErrAutoreject, err), - enforcementAction: c.enforcementAction, + constraint: c.constraint, + error: fmt.Errorf("%w: %v", errors.ErrAutoreject, err), + enforcementAction: c.enforcementAction, + scopedEnforcementActions: actions, } case matches: // Fill in Constraint, so we can pass it to the Driver to run. return &constraintMatchResult{ - constraint: c.constraint, + constraint: c.constraint, + enforcementAction: c.enforcementAction, + scopedEnforcementActions: actions, } default: // No match and no error, so no need to record a result. @@ -68,6 +106,9 @@ type constraintMatchResult struct { // enforcementAction, if specified, is the immediate action to take. // Only filled in if error is non-nil. enforcementAction string + // scopedEnforcementActions are action to take for specific enforcement point. + // Only filled in if error is non-nil. + scopedEnforcementActions []string // error is a problem encountered while attempting to run the Constraint's // Matcher. error error @@ -75,8 +116,9 @@ type constraintMatchResult struct { func (r *constraintMatchResult) ToResult() *types.Result { return &types.Result{ - Msg: r.error.Error(), - Constraint: r.constraint, - EnforcementAction: r.enforcementAction, + Msg: r.error.Error(), + Constraint: r.constraint, + EnforcementAction: r.enforcementAction, + ScopedEnforcementActions: r.scopedEnforcementActions, } } diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/crds/schema.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/crds/schema.go index f13c0dcc9f7..530ae2a42d5 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/crds/schema.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/crds/schema.go @@ -13,6 +13,29 @@ func CreateSchema(templ *templates.ConstraintTemplate, target MatchSchemaProvide props := map[string]apiextensions.JSONSchemaProps{ "match": target.MatchSchema(), "enforcementAction": {Type: "string", Default: &defaultEnforcementAction}, + "scopedEnforcementActions": { + Type: "array", + Default: nil, + Items: &apiextensions.JSONSchemaPropsOrArray{ + Schema: &apiextensions.JSONSchemaProps{ + Type: "object", + Properties: map[string]apiextensions.JSONSchemaProps{ + "action": {Type: "string"}, + "enforcementPoints": { + Type: "array", + Items: &apiextensions.JSONSchemaPropsOrArray{ + Schema: &apiextensions.JSONSchemaProps{ + Type: "object", + Properties: map[string]apiextensions.JSONSchemaProps{ + "name": {Type: "string"}, + }, + }, + }, + }, + }, + }, + }, + }, } if templ.Spec.CRD.Spec.Validation != nil && templ.Spec.CRD.Spec.Validation.OpenAPIV3Schema != nil { diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/interface.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/interface.go index 090c732a53c..651e6e080ca 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/interface.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/interface.go @@ -3,6 +3,7 @@ package drivers import ( "context" + "github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews" "github.com/open-policy-agent/frameworks/constraint/pkg/core/templates" "github.com/open-policy-agent/opa/storage" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -43,7 +44,7 @@ type Driver interface { // Query runs the passed target's Constraints against review. // Returns a QueryResponse type. // Returns an error if there was a problem executing the Query. - Query(ctx context.Context, target string, constraints []*unstructured.Unstructured, review interface{}, opts ...QueryOpt) (*QueryResponse, error) + Query(ctx context.Context, target string, constraints []*unstructured.Unstructured, review interface{}, opts ...reviews.ReviewOpt) (*QueryResponse, error) // Dump outputs the entire state of compiled Templates, added Constraints, and // cached data used for referential Constraints. diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/k8scel/driver.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/k8scel/driver.go index ae0f8333c3a..a12f2e05a35 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/k8scel/driver.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/k8scel/driver.go @@ -8,10 +8,10 @@ import ( "sync" "time" - apiconstraints "github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints" "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers" pSchema "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/k8scel/schema" "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/k8scel/transform" + "github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews" "github.com/open-policy-agent/frameworks/constraint/pkg/core/templates" "github.com/open-policy-agent/frameworks/constraint/pkg/instrumentation" "github.com/open-policy-agent/frameworks/constraint/pkg/types" @@ -150,8 +150,8 @@ func (d *Driver) RemoveData(_ context.Context, _ string, _ storage.Path) error { return nil } -func (d *Driver) Query(ctx context.Context, target string, constraints []*unstructured.Unstructured, review interface{}, opts ...drivers.QueryOpt) (*drivers.QueryResponse, error) { - cfg := &drivers.QueryCfg{} +func (d *Driver) Query(ctx context.Context, target string, constraints []*unstructured.Unstructured, review interface{}, opts ...reviews.ReviewOpt) (*drivers.QueryResponse, error) { + cfg := &reviews.ReviewCfg{} for _, opt := range opts { opt(cfg) } @@ -200,20 +200,12 @@ func (d *Driver) Query(ctx context.Context, target string, constraints []*unstru // TODO: should namespace be made available, if possible? Generally that context should be present response := validator.Validate(ctx, versionedAttr.GetResource(), versionedAttr, constraint, nil, celAPI.PerCallLimit, nil) - enforcementAction, found, err := unstructured.NestedString(constraint.Object, "spec", "enforcementAction") - if err != nil { - return nil, err - } - if !found { - enforcementAction = apiconstraints.EnforcementActionDeny - } for _, decision := range response.Decisions { if decision.Action == validatingadmissionpolicy.ActionDeny { results = append(results, &types.Result{ - Target: target, - Msg: decision.Message, - Constraint: constraint, - EnforcementAction: enforcementAction, + Target: target, + Msg: decision.Message, + Constraint: constraint, }) } } diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/k8scel/transform/make_vap_objects.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/k8scel/transform/make_vap_objects.go index e1493d637bb..3180ee6aeb6 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/k8scel/transform/make_vap_objects.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/k8scel/transform/make_vap_objects.go @@ -75,20 +75,24 @@ func TemplateToPolicyDefinition(template *templates.ConstraintTemplate) (*admiss return policy, nil } -func ConstraintToBinding(constraint *unstructured.Unstructured) (*admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding, error) { - enforcementActionStr, err := apiconstraints.GetEnforcementAction(constraint) - if err != nil { - return nil, err +// ConstraintToBinding converts a Constraint to a ValidatingAdmissionPolicyBinding. +// Accepts a list of enforcement actions to apply to the binding. +// If the enforcement action is not recognized, returns an error. +func ConstraintToBinding(constraint *unstructured.Unstructured, actions []string) (*admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding, error) { + if len(actions) == 0 { + return nil, fmt.Errorf("%w: enforcement actions must be provided", ErrBadEnforcementAction) } + var enforcementActions []admissionregistrationv1beta1.ValidationAction - var enforcementAction admissionregistrationv1beta1.ValidationAction - switch enforcementActionStr { - case apiconstraints.EnforcementActionDeny: - enforcementAction = admissionregistrationv1beta1.Deny - case "warn": - enforcementAction = admissionregistrationv1beta1.Warn - default: - return nil, fmt.Errorf("%w: unrecognized enforcement action %s, must be `warn` or `deny`", ErrBadEnforcementAction, enforcementActionStr) + for _, action := range actions { + switch action { + case apiconstraints.EnforcementActionDeny: + enforcementActions = append(enforcementActions, admissionregistrationv1beta1.Deny) + case "warn": + enforcementActions = append(enforcementActions, admissionregistrationv1beta1.Warn) + default: + return nil, fmt.Errorf("%w: unrecognized enforcement action %s, must be `warn` or `deny`", ErrBadEnforcementAction, action) + } } binding := &admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding{ @@ -102,7 +106,7 @@ func ConstraintToBinding(constraint *unstructured.Unstructured) (*admissionregis ParameterNotFoundAction: ptr.To[admissionregistrationv1beta1.ParameterNotFoundActionType](admissionregistrationv1beta1.AllowAction), }, MatchResources: &admissionregistrationv1beta1.MatchResources{}, - ValidationActions: []admissionregistrationv1beta1.ValidationAction{enforcementAction}, + ValidationActions: enforcementActions, }, } objectSelectorMap, found, err := unstructured.NestedMap(constraint.Object, "spec", "match", "labelSelector") diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/rego/driver.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/rego/driver.go index 44e0884345d..8f40529fa64 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/rego/driver.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/rego/driver.go @@ -15,6 +15,7 @@ import ( "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers" "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/rego/schema" clienterrors "github.com/open-policy-agent/frameworks/constraint/pkg/client/errors" + "github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews" "github.com/open-policy-agent/frameworks/constraint/pkg/core/templates" "github.com/open-policy-agent/frameworks/constraint/pkg/externaldata" "github.com/open-policy-agent/frameworks/constraint/pkg/instrumentation" @@ -196,8 +197,8 @@ func (d *Driver) RemoveData(ctx context.Context, target string, path storage.Pat // input is the already-parsed Rego Value to use as input. // Returns the Rego results, the trace if requested, or an error if there was // a problem executing the query. -func (d *Driver) eval(ctx context.Context, compiler *ast.Compiler, target string, path []string, input ast.Value, opts ...drivers.QueryOpt) (rego.ResultSet, *string, error) { - cfg := &drivers.QueryCfg{} +func (d *Driver) eval(ctx context.Context, compiler *ast.Compiler, target string, path []string, input ast.Value, opts ...reviews.ReviewOpt) (rego.ResultSet, *string, error) { + cfg := &reviews.ReviewCfg{} for _, opt := range opts { opt(cfg) } @@ -241,7 +242,7 @@ func (d *Driver) eval(ctx context.Context, compiler *ast.Compiler, target string return res, t, err } -func (d *Driver) Query(ctx context.Context, target string, constraints []*unstructured.Unstructured, review interface{}, opts ...drivers.QueryOpt) (*drivers.QueryResponse, error) { +func (d *Driver) Query(ctx context.Context, target string, constraints []*unstructured.Unstructured, review interface{}, opts ...reviews.ReviewOpt) (*drivers.QueryResponse, error) { if len(constraints) == 0 { return nil, nil } @@ -264,7 +265,7 @@ func (d *Driver) Query(ctx context.Context, target string, constraints []*unstru d.mtx.RLock() defer d.mtx.RUnlock() - cfg := &drivers.QueryCfg{} + cfg := &reviews.ReviewCfg{} for _, opt := range opts { opt(cfg) } diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/to_result.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/to_result.go index 7f26d183526..72e8af9c690 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/to_result.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/to_result.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" - apiconstraints "github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints" "github.com/open-policy-agent/frameworks/constraint/pkg/types" "github.com/open-policy-agent/opa/rego" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -74,20 +73,10 @@ func ToResult(constraints map[ConstraintKey]*unstructured.Unstructured, r rego.R Kind: keyMap["kind"], Name: keyMap["name"], } - constraint := constraints[key] + // DeepCopy the result so we don't leak internal state. result.Constraint = constraint.DeepCopy() - enforcementAction, found, err := unstructured.NestedString(constraint.Object, "spec", "enforcementAction") - if err != nil { - return nil, err - } - if !found { - enforcementAction = apiconstraints.EnforcementActionDeny - } - - result.EnforcementAction = enforcementAction - return result, nil } diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/new_client.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/new_client.go index 64d153e17dc..0b89f2b875d 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/new_client.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/new_client.go @@ -9,9 +9,10 @@ import ( // NewClient creates a new client. func NewClient(opts ...Opt) (*Client, error) { c := &Client{ - templates: make(map[string]*templateClient), - drivers: make(map[string]drivers.Driver), - driverPriority: make(map[string]int), + templates: make(map[string]*templateClient), + drivers: make(map[string]drivers.Driver), + driverPriority: make(map[string]int), + enforcementPoints: []string{"*"}, } for _, opt := range opts { diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/query_opts.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews/review_opts.go similarity index 51% rename from vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/query_opts.go rename to vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews/review_opts.go index 4b244cc2fc3..32b08d78569 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/query_opts.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews/review_opts.go @@ -1,17 +1,20 @@ -package drivers +package reviews -type QueryCfg struct { +import "strings" + +type ReviewCfg struct { TracingEnabled bool StatsEnabled bool + SourceEP string } -// QueryOpt specifies optional arguments for Query driver calls. -type QueryOpt func(*QueryCfg) +// ReviewOpt specifies optional arguments for Query driver calls. +type ReviewOpt func(*ReviewCfg) // Tracing enables Rego tracing for a single query. // If tracing is enabled for the Driver, Tracing(false) does not disable Tracing. -func Tracing(enabled bool) QueryOpt { - return func(cfg *QueryCfg) { +func Tracing(enabled bool) ReviewOpt { + return func(cfg *ReviewCfg) { cfg.TracingEnabled = enabled } } @@ -19,8 +22,14 @@ func Tracing(enabled bool) QueryOpt { // Stats(true) enables the driver to return evaluation stats for a single // query. If stats is enabled for the Driver at construction time, then // Stats(false) does not disable Stats for this single query. -func Stats(enabled bool) QueryOpt { - return func(cfg *QueryCfg) { +func Stats(enabled bool) ReviewOpt { + return func(cfg *ReviewCfg) { cfg.StatsEnabled = enabled } } + +func SourceEP(ep string) ReviewOpt { + return func(cfg *ReviewCfg) { + cfg.SourceEP = strings.ToLower(ep) + } +} diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/template_client.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/template_client.go index a5ef56c36a8..9017ebb35c9 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/template_client.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/template_client.go @@ -95,10 +95,46 @@ func (e *templateClient) Update(templ *templates.ConstraintTemplate, crd *apiext // Returns true and no error if the Constraint was changed successfully. // Returns false and no error if the Constraint was not updated due to being // identical to the stored version. -func (e *templateClient) AddConstraint(constraint *unstructured.Unstructured) (bool, error) { +func (e *templateClient) AddConstraint(constraint *unstructured.Unstructured, enforcementPoints []string) (bool, error) { + // Initialize a map to hold enforcement actions for each enforcement point (EP) + enforcementActionsForEP := make(map[string][]string) + // Retrieve the enforcement action for the given constraint enforcementAction, err := apiconstraints.GetEnforcementAction(constraint) if err != nil { - return false, err + return false, err // Return an error if unable to get the enforcement action + } + + // Check if the enforcement action is scoped to specific enforcement points + if apiconstraints.IsEnforcementActionScoped(enforcementAction) { + // Retrieve a map of enforcement actions for each EP based on the constraint + enforcementActionsForEPMap, err := apiconstraints.GetEnforcementActionsForEP(constraint, enforcementPoints) + if err != nil { + return false, err // Return an error if unable to get the enforcement actions for EPs + } + // Iterate over the map to populate enforcementActionsForEP + for ep, actions := range enforcementActionsForEPMap { + if len(actions) == 0 { + continue // Skip if there are no actions for the current EP + } + // Initialize a slice to hold actions for the current EP, with capacity equal to the number of actions + enforcementActionsForEP[ep] = make([]string, 0, len(actions)) + // Add each action to the slice for the current EP + for action := range actions { + enforcementActionsForEP[ep] = append(enforcementActionsForEP[ep], action) + } + } + } else { + // If the enforcement action is not scoped, or if client does not specify EPs, apply the action to all EPs + if len(enforcementPoints) == 0 { + enforcementPoints = []string{"*"} // Use "*" to represent all EPs + } + // Iterate over the enforcement points to set the enforcement action for each + for _, ep := range enforcementPoints { + // Initialize a slice with capacity 1 for the enforcement action + enforcementActionsForEP[ep] = make([]string, 0, 1) + // Add the enforcement action to the slice for the current EP + enforcementActionsForEP[ep] = append(enforcementActionsForEP[ep], enforcementAction) + } } // Compare with the already-existing Constraint. @@ -117,9 +153,10 @@ func (e *templateClient) AddConstraint(constraint *unstructured.Unstructured) (b delete(cpy.Object, statusField) e.constraints[constraint.GetName()] = &constraintClient{ - constraint: cpy, - matchers: matchers, - enforcementAction: enforcementAction, + constraint: cpy, + matchers: matchers, + enforcementAction: enforcementAction, + enforcementActionsForEP: enforcementActionsForEP, } return true, nil @@ -144,11 +181,11 @@ func (e *templateClient) RemoveConstraint(name string) { // against the passed review. // // ignoredTargets specifies the targets whose matchers to not run. -func (e *templateClient) Matches(target string, review interface{}) map[string]constraintMatchResult { +func (e *templateClient) Matches(target string, review interface{}, sourceEPs []string) map[string]constraintMatchResult { result := make(map[string]constraintMatchResult) for name, constraint := range e.constraints { - cResult := constraint.matches(target, review) + cResult := constraint.matches(target, review, sourceEPs...) if cResult != nil { result[name] = *cResult } diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/regorewriter/regorewriter.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/regorewriter/regorewriter.go index ab3c9d19c0d..815ae6642f0 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/regorewriter/regorewriter.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/regorewriter/regorewriter.go @@ -162,7 +162,7 @@ func (r *RegoRewriter) addPathFromFs(path string, slice *[]*Module) error { } walkFn := func(path string, info os.FileInfo, _ error) error { - if info.IsDir() || !strings.HasSuffix(path, ".rego") { + if info == nil || (info.IsDir() || !strings.HasSuffix(path, ".rego")) { return nil } return r.addFileFromFs(path, slice) diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/types/validation.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/types/validation.go index f6a5ea4042d..2b40c1bc169 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/types/validation.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/types/validation.go @@ -28,6 +28,9 @@ type Result struct { // The enforcement action of the constraint EnforcementAction string `json:"enforcementAction,omitempty"` + + // The scoped actions of the constraint + ScopedEnforcementActions []string `json:"scopedActions,omitempty"` } // Response is a collection of Constraint violations for a particular Target. diff --git a/vendor/github.com/open-policy-agent/opa/ast/annotations.go b/vendor/github.com/open-policy-agent/opa/ast/annotations.go index 18686855424..9663b0cc672 100644 --- a/vendor/github.com/open-policy-agent/opa/ast/annotations.go +++ b/vendor/github.com/open-policy-agent/opa/ast/annotations.go @@ -509,6 +509,34 @@ func (a *Annotations) toObject() (*Object, *Error) { return &obj, nil } +func attachRuleAnnotations(mod *Module) { + // make a copy of the annotations + cpy := make([]*Annotations, len(mod.Annotations)) + for i, a := range mod.Annotations { + cpy[i] = a.Copy(a.node) + } + + for _, rule := range mod.Rules { + var j int + var found bool + for i, a := range cpy { + if rule.Ref().Equal(a.GetTargetPath()) { + if a.Scope == annotationScopeDocument { + rule.Annotations = append(rule.Annotations, a) + } else if a.Scope == annotationScopeRule && rule.Loc().Row > a.Location.Row { + j = i + found = true + rule.Annotations = append(rule.Annotations, a) + } + } + } + + if found && j < len(cpy) { + cpy = append(cpy[:j], cpy[j+1:]...) + } + } +} + func attachAnnotationsNodes(mod *Module) Errors { var errs Errors @@ -599,9 +627,8 @@ func (a *AuthorAnnotation) String() string { return a.Name } else if len(a.Name) == 0 { return fmt.Sprintf("<%s>", a.Email) - } else { - return fmt.Sprintf("%s <%s>", a.Name, a.Email) } + return fmt.Sprintf("%s <%s>", a.Name, a.Email) } // Copy returns a deep copy of rr. diff --git a/vendor/github.com/open-policy-agent/opa/ast/compile.go b/vendor/github.com/open-policy-agent/opa/ast/compile.go index 422ba468deb..c59cfede620 100644 --- a/vendor/github.com/open-policy-agent/opa/ast/compile.go +++ b/vendor/github.com/open-policy-agent/opa/ast/compile.go @@ -120,34 +120,35 @@ type Compiler struct { // Capabliities required by the modules that were compiled. Required *Capabilities - localvargen *localVarGenerator - moduleLoader ModuleLoader - ruleIndices *util.HashMap - stages []stage - maxErrs int - sorted []string // list of sorted module names - pathExists func([]string) (bool, error) - after map[string][]CompilerStageDefinition - metrics metrics.Metrics - capabilities *Capabilities // user-supplied capabilities - imports map[string][]*Import // saved imports from stripping - builtins map[string]*Builtin // universe of built-in functions - customBuiltins map[string]*Builtin // user-supplied custom built-in functions (deprecated: use capabilities) - unsafeBuiltinsMap map[string]struct{} // user-supplied set of unsafe built-ins functions to block (deprecated: use capabilities) - deprecatedBuiltinsMap map[string]struct{} // set of deprecated, but not removed, built-in functions - enablePrintStatements bool // indicates if print statements should be elided (default) - comprehensionIndices map[*Term]*ComprehensionIndex // comprehension key index - initialized bool // indicates if init() has been called - debug debug.Debug // emits debug information produced during compilation - schemaSet *SchemaSet // user-supplied schemas for input and data documents - inputType types.Type // global input type retrieved from schema set - annotationSet *AnnotationSet // hierarchical set of annotations - strict bool // enforce strict compilation checks - keepModules bool // whether to keep the unprocessed, parse modules (below) - parsedModules map[string]*Module // parsed, but otherwise unprocessed modules, kept track of when keepModules is true - useTypeCheckAnnotations bool // whether to provide annotated information (schemas) to the type checker - allowUndefinedFuncCalls bool // don't error on calls to unknown functions. - evalMode CompilerEvalMode + localvargen *localVarGenerator + moduleLoader ModuleLoader + ruleIndices *util.HashMap + stages []stage + maxErrs int + sorted []string // list of sorted module names + pathExists func([]string) (bool, error) + after map[string][]CompilerStageDefinition + metrics metrics.Metrics + capabilities *Capabilities // user-supplied capabilities + imports map[string][]*Import // saved imports from stripping + builtins map[string]*Builtin // universe of built-in functions + customBuiltins map[string]*Builtin // user-supplied custom built-in functions (deprecated: use capabilities) + unsafeBuiltinsMap map[string]struct{} // user-supplied set of unsafe built-ins functions to block (deprecated: use capabilities) + deprecatedBuiltinsMap map[string]struct{} // set of deprecated, but not removed, built-in functions + enablePrintStatements bool // indicates if print statements should be elided (default) + comprehensionIndices map[*Term]*ComprehensionIndex // comprehension key index + initialized bool // indicates if init() has been called + debug debug.Debug // emits debug information produced during compilation + schemaSet *SchemaSet // user-supplied schemas for input and data documents + inputType types.Type // global input type retrieved from schema set + annotationSet *AnnotationSet // hierarchical set of annotations + strict bool // enforce strict compilation checks + keepModules bool // whether to keep the unprocessed, parse modules (below) + parsedModules map[string]*Module // parsed, but otherwise unprocessed modules, kept track of when keepModules is true + useTypeCheckAnnotations bool // whether to provide annotated information (schemas) to the type checker + allowUndefinedFuncCalls bool // don't error on calls to unknown functions. + evalMode CompilerEvalMode // + rewriteTestRulesForTracing bool // rewrite test rules to capture dynamic values for tracing. } // CompilerStage defines the interface for stages in the compiler. @@ -346,6 +347,7 @@ func NewCompiler() *Compiler { {"CheckSafetyRuleBodies", "compile_stage_check_safety_rule_bodies", c.checkSafetyRuleBodies}, {"RewriteEquals", "compile_stage_rewrite_equals", c.rewriteEquals}, {"RewriteDynamicTerms", "compile_stage_rewrite_dynamic_terms", c.rewriteDynamicTerms}, + {"RewriteTestRulesForTracing", "compile_stage_rewrite_test_rules_for_tracing", c.rewriteTestRuleEqualities}, // must run after RewriteDynamicTerms {"CheckRecursion", "compile_stage_check_recursion", c.checkRecursion}, {"CheckTypes", "compile_stage_check_types", c.checkTypes}, // must be run after CheckRecursion {"CheckUnsafeBuiltins", "compile_state_check_unsafe_builtins", c.checkUnsafeBuiltins}, @@ -469,6 +471,13 @@ func (c *Compiler) WithEvalMode(e CompilerEvalMode) *Compiler { return c } +// WithRewriteTestRules enables rewriting test rules to capture dynamic values in local variables, +// so they can be accessed by tracing. +func (c *Compiler) WithRewriteTestRules(rewrite bool) *Compiler { + c.rewriteTestRulesForTracing = rewrite + return c +} + // ParsedModules returns the parsed, unprocessed modules from the compiler. // It is `nil` if keeping modules wasn't enabled via `WithKeepModules(true)`. // The map includes all modules loaded via the ModuleLoader, if one was used. @@ -2167,6 +2176,43 @@ func (c *Compiler) rewriteDynamicTerms() { } } +// rewriteTestRuleEqualities rewrites equality expressions in test rule bodies to create local vars for statements that would otherwise +// not have their values captured through tracing, such as refs and comprehensions not unified/assigned to a local var. +// For example, given the following module: +// +// package test +// +// p.q contains v if { +// some v in numbers.range(1, 3) +// } +// +// p.r := "foo" +// +// test_rule { +// p == { +// "q": {4, 5, 6} +// } +// } +// +// `p` in `test_rule` resolves to `data.test.p`, which won't be an entry in the virtual-cache and must therefore be calculated after-the-fact. +// If `p` isn't captured in a local var, there is no trivial way to retrieve its value for test reporting. +func (c *Compiler) rewriteTestRuleEqualities() { + if !c.rewriteTestRulesForTracing { + return + } + + f := newEqualityFactory(c.localvargen) + for _, name := range c.sorted { + mod := c.Modules[name] + WalkRules(mod, func(rule *Rule) bool { + if strings.HasPrefix(string(rule.Head.Name), "test_") { + rule.Body = rewriteTestEqualities(f, rule.Body) + } + return false + }) + } +} + func (c *Compiler) parseMetadataBlocks() { // Only parse annotations if rego.metadata built-ins are called regoMetadataCalled := false @@ -2196,6 +2242,8 @@ func (c *Compiler) parseMetadataBlocks() { for _, err := range errs { c.err(err) } + + attachRuleAnnotations(mod) } } } @@ -4192,7 +4240,7 @@ func resolveRefsInRule(globals map[Var]*usedRef, rule *Rule) error { // Object keys cannot be pattern matched so only walk values. case *object: - x.Foreach(func(k, v *Term) { + x.Foreach(func(_, v *Term) { vis.Walk(v) }) @@ -4515,6 +4563,41 @@ func rewriteEquals(x interface{}) (modified bool) { return modified } +func rewriteTestEqualities(f *equalityFactory, body Body) Body { + result := make(Body, 0, len(body)) + for _, expr := range body { + // We can't rewrite negated expressions; if the extracted term is undefined, evaluation would fail before + // reaching the negation check. + if !expr.Negated && !expr.Generated { + switch { + case expr.IsEquality(): + terms := expr.Terms.([]*Term) + result, terms[1] = rewriteDynamicsShallow(expr, f, terms[1], result) + result, terms[2] = rewriteDynamicsShallow(expr, f, terms[2], result) + case expr.IsEvery(): + // We rewrite equalities inside of every-bodies as a fail here will be the cause of the test-rule fail. + // Failures inside other expressions with closures, such as comprehensions, won't cause the test-rule to fail, so we skip those. + every := expr.Terms.(*Every) + every.Body = rewriteTestEqualities(f, every.Body) + } + } + result = appendExpr(result, expr) + } + return result +} + +func rewriteDynamicsShallow(original *Expr, f *equalityFactory, term *Term, result Body) (Body, *Term) { + switch term.Value.(type) { + case Ref, *ArrayComprehension, *SetComprehension, *ObjectComprehension: + generated := f.Generate(term) + generated.With = original.With + result.Append(generated) + connectGeneratedExprs(original, generated) + return result, result[len(result)-1].Operand(0) + } + return result, term +} + // rewriteDynamics will rewrite the body so that dynamic terms (i.e., refs and // comprehensions) are bound to vars earlier in the query. This translation // results in eager evaluation. @@ -4606,6 +4689,7 @@ func rewriteDynamicsOne(original *Expr, f *equalityFactory, term *Term, result B generated := f.Generate(term) generated.With = original.With result.Append(generated) + connectGeneratedExprs(original, generated) return result, result[len(result)-1].Operand(0) case *Array: for i := 0; i < v.Len(); i++ { @@ -4634,16 +4718,19 @@ func rewriteDynamicsOne(original *Expr, f *equalityFactory, term *Term, result B var extra *Expr v.Body, extra = rewriteDynamicsComprehensionBody(original, f, v.Body, term) result.Append(extra) + connectGeneratedExprs(original, extra) 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) 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) return result, result[len(result)-1].Operand(0) } return result, term @@ -4711,6 +4798,7 @@ func expandExpr(gen *localVarGenerator, expr *Expr) (result []*Expr) { for i := 1; i < len(terms); i++ { var extras []*Expr extras, terms[i] = expandExprTerm(gen, terms[i]) + connectGeneratedExprs(expr, extras...) if len(expr.With) > 0 { for i := range extras { extras[i].With = expr.With @@ -4721,16 +4809,14 @@ func expandExpr(gen *localVarGenerator, expr *Expr) (result []*Expr) { result = append(result, expr) case *Every: var extras []*Expr - if _, ok := terms.Domain.Value.(Call); ok { - extras, terms.Domain = expandExprTerm(gen, terms.Domain) - } else { - term := NewTerm(gen.Generate()).SetLocation(terms.Domain.Location) - eq := Equality.Expr(term, terms.Domain).SetLocation(terms.Domain.Location) - eq.Generated = true - eq.With = expr.With - extras = append(extras, eq) - terms.Domain = term - } + + term := NewTerm(gen.Generate()).SetLocation(terms.Domain.Location) + eq := Equality.Expr(term, terms.Domain).SetLocation(terms.Domain.Location) + eq.Generated = true + eq.With = expr.With + extras = expandExpr(gen, eq) + terms.Domain = term + terms.Body = rewriteExprTermsInBody(gen, terms.Body) result = append(result, extras...) result = append(result, expr) @@ -4738,6 +4824,13 @@ func expandExpr(gen *localVarGenerator, expr *Expr) (result []*Expr) { return } +func connectGeneratedExprs(parent *Expr, children ...*Expr) { + for _, child := range children { + child.generatedFrom = parent + parent.generates = append(parent.generates, child) + } +} + func expandExprTerm(gen *localVarGenerator, term *Term) (support []*Expr, output *Term) { output = term switch v := term.Value.(type) { @@ -5492,26 +5585,34 @@ func validateWith(c *Compiler, unsafeBuiltinsMap map[string]struct{}, expr *Expr return false, err } + isAllowedUnknownFuncCall := false + if c.allowUndefinedFuncCalls { + switch target.Value.(type) { + case Ref, Var: + isAllowedUnknownFuncCall = true + } + } + switch { case isDataRef(target): ref := target.Value.(Ref) - node := c.RuleTree + targetNode := c.RuleTree for i := 0; i < len(ref)-1; i++ { - child := node.Child(ref[i].Value) + child := targetNode.Child(ref[i].Value) if child == nil { break } else if len(child.Values) > 0 { return false, NewError(CompileErr, target.Loc(), "with keyword cannot partially replace virtual document(s)") } - node = child + targetNode = child } - if node != nil { + if targetNode != nil { // NOTE(sr): at this point in the compiler stages, we don't have a fully-populated // TypeEnv yet -- so we have to make do with this check to see if the replacement // target is a function. It's probably wrong for arity-0 functions, but those are // and edge case anyways. - if child := node.Child(ref[len(ref)-1].Value); child != nil { + if child := targetNode.Child(ref[len(ref)-1].Value); child != nil { for _, v := range child.Values { if len(v.(*Rule).Head.Args) > 0 { if ok, err := validateWithFunctionValue(c.builtins, unsafeBuiltinsMap, c.RuleTree, value); err != nil || ok { @@ -5521,6 +5622,18 @@ func validateWith(c *Compiler, unsafeBuiltinsMap map[string]struct{}, expr *Expr } } } + + // If the with-value is a ref to a function, but not a call, we can't rewrite it + if r, ok := value.Value.(Ref); ok { + // TODO: check that target ref doesn't exist? + if valueNode := c.RuleTree.Find(r); valueNode != nil { + for _, v := range valueNode.Values { + if len(v.(*Rule).Head.Args) > 0 { + return false, nil + } + } + } + } case isInputRef(target): // ok, valid case isBuiltinRefOrVar: @@ -5539,6 +5652,9 @@ func validateWith(c *Compiler, unsafeBuiltinsMap map[string]struct{}, expr *Expr if ok, err := validateWithFunctionValue(c.builtins, unsafeBuiltinsMap, c.RuleTree, value); err != nil || ok { return false, err // err may be nil } + case isAllowedUnknownFuncCall: + // The target isn't a ref to the input doc, data doc, or a known built-in, but it might be a ref to an unknown built-in. + return false, nil default: return false, NewError(TypeErr, target.Location, "with keyword target must reference existing %v, %v, or a function", InputRootDocument, DefaultRootDocument) } diff --git a/vendor/github.com/open-policy-agent/opa/ast/env.go b/vendor/github.com/open-policy-agent/opa/ast/env.go index 784a34c0470..c767aafefb6 100644 --- a/vendor/github.com/open-policy-agent/opa/ast/env.go +++ b/vendor/github.com/open-policy-agent/opa/ast/env.go @@ -472,7 +472,7 @@ func insertIntoObject(o *types.Object, path Ref, tpe types.Type, env *TypeEnv) ( func (n *typeTreeNode) Leafs() map[*Ref]types.Type { leafs := map[*Ref]types.Type{} - n.children.Iter(func(k, v util.T) bool { + n.children.Iter(func(_, v util.T) bool { collectLeafs(v.(*typeTreeNode), nil, leafs) return false }) @@ -485,7 +485,7 @@ func collectLeafs(n *typeTreeNode, path Ref, leafs map[*Ref]types.Type) { leafs[&nPath] = n.Value() return } - n.children.Iter(func(k, v util.T) bool { + n.children.Iter(func(_, v util.T) bool { collectLeafs(v.(*typeTreeNode), nPath, leafs) return false }) diff --git a/vendor/github.com/open-policy-agent/opa/ast/index.go b/vendor/github.com/open-policy-agent/opa/ast/index.go index e14bb72eefc..8cad71f1ec6 100644 --- a/vendor/github.com/open-policy-agent/opa/ast/index.go +++ b/vendor/github.com/open-policy-agent/opa/ast/index.go @@ -164,7 +164,7 @@ func (i *baseDocEqIndex) Lookup(resolver ValueResolver) (*IndexResult, error) { return result, nil } -func (i *baseDocEqIndex) AllRules(resolver ValueResolver) (*IndexResult, error) { +func (i *baseDocEqIndex) AllRules(_ ValueResolver) (*IndexResult, error) { tr := newTrieTraversalResult() // Walk over the rule trie and accumulate _all_ rules diff --git a/vendor/github.com/open-policy-agent/opa/ast/internal/scanner/scanner.go b/vendor/github.com/open-policy-agent/opa/ast/internal/scanner/scanner.go index 97ee8bde01c..a0200ac18dd 100644 --- a/vendor/github.com/open-policy-agent/opa/ast/internal/scanner/scanner.go +++ b/vendor/github.com/open-policy-agent/opa/ast/internal/scanner/scanner.go @@ -26,6 +26,7 @@ type Scanner struct { width int errors []Error keywords map[string]tokens.Token + tabs []int regoV1Compatible bool } @@ -37,10 +38,11 @@ type Error struct { // Position represents a point in the scanned source code. type Position struct { - Offset int // start offset in bytes - End int // end offset in bytes - Row int // line number computed in bytes - Col int // column number computed in bytes + Offset int // start offset in bytes + End int // end offset in bytes + Row int // line number computed in bytes + Col int // column number computed in bytes + Tabs []int // positions of any tabs preceding Col } // New returns an initialized scanner that will scan @@ -60,6 +62,7 @@ func New(r io.Reader) (*Scanner, error) { curr: -1, width: 0, keywords: tokens.Keywords(), + tabs: []int{}, } s.next() @@ -156,7 +159,7 @@ func (s *Scanner) WithoutKeywords(kws map[string]tokens.Token) (*Scanner, map[st // for any errors before using the other values. func (s *Scanner) Scan() (tokens.Token, Position, string, []Error) { - pos := Position{Offset: s.offset - s.width, Row: s.row, Col: s.col} + pos := Position{Offset: s.offset - s.width, Row: s.row, Col: s.col, Tabs: s.tabs} var tok tokens.Token var lit string @@ -410,8 +413,12 @@ func (s *Scanner) next() { if s.curr == '\n' { s.row++ s.col = 0 + s.tabs = []int{} } else { s.col++ + if s.curr == '\t' { + s.tabs = append(s.tabs, s.col) + } } } diff --git a/vendor/github.com/open-policy-agent/opa/ast/location/location.go b/vendor/github.com/open-policy-agent/opa/ast/location/location.go index 309351a1ed2..92226df3f0b 100644 --- a/vendor/github.com/open-policy-agent/opa/ast/location/location.go +++ b/vendor/github.com/open-policy-agent/opa/ast/location/location.go @@ -20,6 +20,8 @@ type Location struct { // JSONOptions specifies options for marshaling and unmarshalling of locations JSONOptions astJSON.Options + + Tabs []int `json:"-"` // The column offsets of tabs in the source. } // NewLocation returns a new Location object. diff --git a/vendor/github.com/open-policy-agent/opa/ast/parser.go b/vendor/github.com/open-policy-agent/opa/ast/parser.go index 10479caffd6..388e5e59261 100644 --- a/vendor/github.com/open-policy-agent/opa/ast/parser.go +++ b/vendor/github.com/open-policy-agent/opa/ast/parser.go @@ -943,12 +943,10 @@ func (p *Parser) parseHead(defaultRule bool) (*Head, bool) { p.illegal("expected rule value term (e.g., %s[%s] = { ... })", name, head.Key) } case tokens.Assign: - s := p.save() p.scan() head.Assign = true head.Value = p.parseTermInfixCall() if head.Value == nil { - p.restore(s) switch { case len(head.Args) > 0: p.illegal("expected function value term (e.g., %s(...) := { ... })", name) @@ -2132,6 +2130,7 @@ func (p *Parser) doScan(skipws bool) { p.s.loc.Col = pos.Col p.s.loc.Offset = pos.Offset 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) @@ -2308,6 +2307,11 @@ func (b *metadataParser) Parse() (*Annotations, error) { b.loc = comment.Location } } + + if match == nil && len(b.comments) > 0 { + b.loc = b.comments[0].Location + } + return nil, augmentYamlError(err, b.comments) } @@ -2375,6 +2379,21 @@ func (b *metadataParser) Parse() (*Annotations, error) { } result.Location = b.loc + + // recreate original text of entire metadata block for location text attribute + sb := strings.Builder{} + sb.WriteString("# METADATA\n") + + lines := bytes.Split(b.buf.Bytes(), []byte{'\n'}) + + for _, line := range lines[:len(lines)-1] { + sb.WriteString("# ") + sb.Write(line) + sb.WriteByte('\n') + } + + result.Location.Text = []byte(strings.TrimSuffix(sb.String(), "\n")) + return &result, nil } @@ -2412,10 +2431,11 @@ func augmentYamlError(err error, comments []*Comment) error { return err } -func unwrapPair(pair map[string]interface{}) (k string, v interface{}) { - for k, v = range pair { +func unwrapPair(pair map[string]interface{}) (string, interface{}) { + for k, v := range pair { + return k, v } - return + return "", nil } var errInvalidSchemaRef = fmt.Errorf("invalid schema reference") diff --git a/vendor/github.com/open-policy-agent/opa/ast/parser_ext.go b/vendor/github.com/open-policy-agent/opa/ast/parser_ext.go index 19af82f5b2f..afaa1d890cc 100644 --- a/vendor/github.com/open-policy-agent/opa/ast/parser_ext.go +++ b/vendor/github.com/open-policy-agent/opa/ast/parser_ext.go @@ -713,6 +713,8 @@ func parseModule(filename string, stmts []Statement, comments []*Comment, regoCo return nil, errs } + attachRuleAnnotations(mod) + return mod, nil } diff --git a/vendor/github.com/open-policy-agent/opa/ast/policy.go b/vendor/github.com/open-policy-agent/opa/ast/policy.go index 051eccc1e6d..d8e6fa3bc46 100644 --- a/vendor/github.com/open-policy-agent/opa/ast/policy.go +++ b/vendor/github.com/open-policy-agent/opa/ast/policy.go @@ -185,11 +185,12 @@ type ( // Rule represents a rule as defined in the language. Rules define the // content of documents that represent policy decisions. Rule struct { - Default bool `json:"default,omitempty"` - Head *Head `json:"head"` - Body Body `json:"body"` - Else *Rule `json:"else,omitempty"` - Location *Location `json:"location,omitempty"` + Default bool `json:"default,omitempty"` + Head *Head `json:"head"` + Body Body `json:"body"` + Else *Rule `json:"else,omitempty"` + Location *Location `json:"location,omitempty"` + Annotations []*Annotations `json:"annotations,omitempty"` // Module is a pointer to the module containing this rule. If the rule // was NOT created while parsing/constructing a module, this should be @@ -232,7 +233,9 @@ type ( Negated bool `json:"negated,omitempty"` Location *Location `json:"location,omitempty"` - jsonOptions astJSON.Options + jsonOptions astJSON.Options + generatedFrom *Expr + generates []*Expr } // SomeDecl represents a variable declaration statement. The symbols are variables. @@ -309,8 +312,8 @@ func (mod *Module) Copy() *Module { nodes[mod.Package] = cpy.Package cpy.Annotations = make([]*Annotations, len(mod.Annotations)) - for i := range mod.Annotations { - cpy.Annotations[i] = mod.Annotations[i].Copy(nodes[mod.Annotations[i].node]) + for i, a := range mod.Annotations { + cpy.Annotations[i] = a.Copy(nodes[a.node]) } cpy.Comments = make([]*Comment, len(mod.Comments)) @@ -663,6 +666,11 @@ func (rule *Rule) Compare(other *Rule) int { if cmp := rule.Body.Compare(other.Body); cmp != 0 { return cmp } + + if cmp := annotationsCompare(rule.Annotations, other.Annotations); cmp != 0 { + return cmp + } + return rule.Else.Compare(other.Else) } @@ -671,6 +679,12 @@ func (rule *Rule) Copy() *Rule { cpy := *rule cpy.Head = rule.Head.Copy() cpy.Body = rule.Body.Copy() + + cpy.Annotations = make([]*Annotations, len(rule.Annotations)) + for i, a := range rule.Annotations { + cpy.Annotations[i] = a.Copy(&cpy) + } + if cpy.Else != nil { cpy.Else = rule.Else.Copy() } @@ -763,6 +777,10 @@ func (rule *Rule) MarshalJSON() ([]byte, error) { } } + if len(rule.Annotations) != 0 { + data["annotations"] = rule.Annotations + } + return json.Marshal(data) } @@ -1577,6 +1595,46 @@ func NewBuiltinExpr(terms ...*Term) *Expr { return &Expr{Terms: terms} } +func (expr *Expr) CogeneratedExprs() []*Expr { + visited := map[*Expr]struct{}{} + visitCogeneratedExprs(expr, func(e *Expr) bool { + if expr.Equal(e) { + return true + } + if _, ok := visited[e]; ok { + return true + } + visited[e] = struct{}{} + return false + }) + + result := make([]*Expr, 0, len(visited)) + for e := range visited { + result = append(result, e) + } + return result +} + +func (expr *Expr) BaseCogeneratedExpr() *Expr { + if expr.generatedFrom == nil { + return expr + } + return expr.generatedFrom.BaseCogeneratedExpr() +} + +func visitCogeneratedExprs(expr *Expr, f func(*Expr) bool) { + if parent := expr.generatedFrom; parent != nil { + if stop := f(parent); !stop { + visitCogeneratedExprs(parent, f) + } + } + for _, child := range expr.generates { + if stop := f(child); !stop { + visitCogeneratedExprs(child, f) + } + } +} + func (d *SomeDecl) String() string { if call, ok := d.Symbols[0].Value.(Call); ok { if len(call) == 4 { diff --git a/vendor/github.com/open-policy-agent/opa/ast/term.go b/vendor/github.com/open-policy-agent/opa/ast/term.go index bb39c18e3b4..4664bc5dac7 100644 --- a/vendor/github.com/open-policy-agent/opa/ast/term.go +++ b/vendor/github.com/open-policy-agent/opa/ast/term.go @@ -2633,7 +2633,7 @@ func filterObject(o Value, filter Value) (Value, error) { other = v } - err := iterObj.Iter(func(key *Term, value *Term) error { + err := iterObj.Iter(func(key *Term, _ *Term) error { if other.Get(key) != nil { filteredValue, err := filterObject(v.Get(key).Value, filteredObj.Get(key).Value) if err != nil { @@ -3091,12 +3091,12 @@ func unmarshalTermSlice(s []interface{}) ([]*Term, error) { buf := []*Term{} for _, x := range s { if m, ok := x.(map[string]interface{}); ok { - if t, err := unmarshalTerm(m); err == nil { + t, err := unmarshalTerm(m) + if err == nil { buf = append(buf, t) continue - } else { - return nil, err } + return nil, err } return nil, fmt.Errorf("ast: unable to unmarshal term") } diff --git a/vendor/github.com/open-policy-agent/opa/ast/visit.go b/vendor/github.com/open-policy-agent/opa/ast/visit.go index 7b0d3b08e76..d83c31149ed 100644 --- a/vendor/github.com/open-policy-agent/opa/ast/visit.go +++ b/vendor/github.com/open-policy-agent/opa/ast/visit.go @@ -352,12 +352,12 @@ func (vis *GenericVisitor) Walk(x interface{}) { vis.Walk(x[i]) } case *object: - x.Foreach(func(k, v *Term) { + x.Foreach(func(k, _ *Term) { vis.Walk(k) vis.Walk(x.Get(k)) }) case Object: - x.Foreach(func(k, v *Term) { + x.Foreach(func(k, _ *Term) { vis.Walk(k) vis.Walk(x.Get(k)) }) @@ -492,12 +492,12 @@ func (vis *BeforeAfterVisitor) Walk(x interface{}) { vis.Walk(x[i]) } case *object: - x.Foreach(func(k, v *Term) { + x.Foreach(func(k, _ *Term) { vis.Walk(k) vis.Walk(x.Get(k)) }) case Object: - x.Foreach(func(k, v *Term) { + x.Foreach(func(k, _ *Term) { vis.Walk(k) vis.Walk(x.Get(k)) }) @@ -579,7 +579,7 @@ func (vis *VarVisitor) Vars() VarSet { func (vis *VarVisitor) visit(v interface{}) bool { if vis.params.SkipObjectKeys { if o, ok := v.(Object); ok { - o.Foreach(func(k, v *Term) { + o.Foreach(func(_, v *Term) { vis.Walk(v) }) return true @@ -741,7 +741,7 @@ func (vis *VarVisitor) Walk(x interface{}) { vis.Walk(x[i]) } case *object: - x.Foreach(func(k, v *Term) { + x.Foreach(func(k, _ *Term) { vis.Walk(k) vis.Walk(x.Get(k)) }) diff --git a/vendor/github.com/open-policy-agent/opa/bundle/bundle.go b/vendor/github.com/open-policy-agent/opa/bundle/bundle.go index 9c02568b535..a68c3c7125b 100644 --- a/vendor/github.com/open-policy-agent/opa/bundle/bundle.go +++ b/vendor/github.com/open-policy-agent/opa/bundle/bundle.go @@ -15,6 +15,7 @@ import ( "fmt" "io" "net/url" + "os" "path" "path/filepath" "reflect" @@ -1190,7 +1191,8 @@ func (b *Bundle) SetRegoVersion(v ast.RegoVersion) { // If there is no defined version for the given path, the default version def is returned. // If the version does not correspond to ast.RegoV0 or ast.RegoV1, an error is returned. func (b *Bundle) RegoVersionForFile(path string, def ast.RegoVersion) (ast.RegoVersion, error) { - if version, err := b.Manifest.numericRegoVersionForFile(path); err != nil { + version, err := b.Manifest.numericRegoVersionForFile(path) + if err != nil { return def, err } else if version == nil { return def, nil @@ -1198,9 +1200,8 @@ func (b *Bundle) RegoVersionForFile(path string, def ast.RegoVersion) (ast.RegoV return ast.RegoV0, nil } else if *version == 1 { return ast.RegoV1, nil - } else { - return def, fmt.Errorf("unknown bundle rego-version %d for file '%s'", *version, path) } + return def, fmt.Errorf("unknown bundle rego-version %d for file '%s'", *version, path) } func (m *Manifest) numericRegoVersionForFile(path string) (*int, error) { @@ -1667,6 +1668,7 @@ func preProcessBundle(loader DirectoryLoader, skipVerify bool, sizeLimitBytes in } func readFile(f *Descriptor, sizeLimitBytes int64) (bytes.Buffer, error) { + // Case for pre-loaded byte buffers, like those from the tarballLoader. if bb, ok := f.reader.(*bytes.Buffer); ok { _ = f.Close() // always close, even on error @@ -1678,6 +1680,37 @@ func readFile(f *Descriptor, sizeLimitBytes int64) (bytes.Buffer, error) { return *bb, nil } + // Case for *lazyFile readers: + if lf, ok := f.reader.(*lazyFile); ok { + var buf bytes.Buffer + if lf.file == nil { + var err error + if lf.file, err = os.Open(lf.path); err != nil { + return buf, fmt.Errorf("failed to open file %s: %w", f.path, err) + } + } + // Bail out if we can't read the whole file-- there's nothing useful we can do at that point! + fileSize, _ := fstatFileSize(lf.file) + if fileSize > sizeLimitBytes { + return buf, fmt.Errorf(maxSizeLimitBytesErrMsg, strings.TrimPrefix(f.Path(), "/"), fileSize, sizeLimitBytes-1) + } + // Prealloc the buffer for the file read. + buffer := make([]byte, fileSize) + _, err := io.ReadFull(lf.file, buffer) + if err != nil { + return buf, err + } + _ = lf.file.Close() // always close, even on error + + // Note(philipc): Replace the lazyFile reader in the *Descriptor with a + // pointer to the wrapping bytes.Buffer, so that we don't re-read the + // file on disk again by accident. + buf = *bytes.NewBuffer(buffer) + f.reader = &buf + return buf, nil + } + + // Fallback case: var buf bytes.Buffer n, err := f.Read(&buf, sizeLimitBytes) _ = f.Close() // always close, even on error @@ -1691,6 +1724,17 @@ func readFile(f *Descriptor, sizeLimitBytes int64) (bytes.Buffer, error) { return buf, nil } +// Takes an already open file handle and invokes the os.Stat system call on it +// to determine the file's size. Passes any errors from *File.Stat on up to the +// caller. +func fstatFileSize(f *os.File) (int64, error) { + fileInfo, err := f.Stat() + if err != nil { + return 0, err + } + return fileInfo.Size(), nil +} + func normalizePath(p string) string { return filepath.ToSlash(p) } diff --git a/vendor/github.com/open-policy-agent/opa/bundle/file.go b/vendor/github.com/open-policy-agent/opa/bundle/file.go index 62ffeeff5fd..c2c5a6b8490 100644 --- a/vendor/github.com/open-policy-agent/opa/bundle/file.go +++ b/vendor/github.com/open-policy-agent/opa/bundle/file.go @@ -211,7 +211,7 @@ func (d *dirLoader) NextFile() (*Descriptor, error) { // build a list of all files we will iterate over and read, but only one time if d.files == nil { d.files = []string{} - err := filepath.Walk(d.root, func(path string, info os.FileInfo, err error) error { + err := filepath.Walk(d.root, func(path string, info os.FileInfo, _ error) error { if info != nil && info.Mode().IsRegular() { if d.filter != nil && d.filter(filepath.ToSlash(path), info, getdepth(path, false)) { return nil @@ -370,12 +370,13 @@ func (t *tarballLoader) NextFile() (*Descriptor, error) { f := file{name: header.Name} - var buf bytes.Buffer - if _, err := io.Copy(&buf, t.tr); err != nil { + // Note(philipc): We rely on the previous size check in this loop for safety. + buf := bytes.NewBuffer(make([]byte, 0, header.Size)) + if _, err := io.Copy(buf, t.tr); err != nil { return nil, fmt.Errorf("failed to copy file %s: %w", header.Name, err) } - f.reader = &buf + f.reader = buf t.files = append(t.files, f) } else if header.Typeflag == tar.TypeDir { diff --git a/vendor/github.com/open-policy-agent/opa/capabilities/v0.65.0.json b/vendor/github.com/open-policy-agent/opa/capabilities/v0.65.0.json new file mode 100644 index 00000000000..06c04773c18 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/capabilities/v0.65.0.json @@ -0,0 +1,4826 @@ +{ + "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" + } + }, + { + "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" + } + }, + { + "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.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" + } + }, + { + "name": "cast_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "cast_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "null" + }, + "type": "function" + } + }, + { + "name": "cast_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "cast_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "cast_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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": "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_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": { + "type": "any" + }, + "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": { + "type": "any" + }, + "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" + } + }, + { + "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": { + "type": "any" + }, + "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": { + "type": "any" + }, + "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": "any" + }, + "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": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "object.union_n", + "decl": { + "args": [ + { + "dynamic": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "result": { + "type": "any" + }, + "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" + } + }, + { + "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" + } + }, + { + "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.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": "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": "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": [ + "contains", + "every", + "if", + "in" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "rule_head_ref_string_prefixes", + "rule_head_refs", + "rego_v1_import" + ] +} diff --git a/vendor/github.com/open-policy-agent/opa/capabilities/v0.66.0.json b/vendor/github.com/open-policy-agent/opa/capabilities/v0.66.0.json new file mode 100644 index 00000000000..06c04773c18 --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/capabilities/v0.66.0.json @@ -0,0 +1,4826 @@ +{ + "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" + } + }, + { + "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" + } + }, + { + "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.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" + } + }, + { + "name": "cast_boolean", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "boolean" + }, + "type": "function" + } + }, + { + "name": "cast_null", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "null" + }, + "type": "function" + } + }, + { + "name": "cast_object", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "function" + } + }, + { + "name": "cast_set", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "of": { + "type": "any" + }, + "type": "set" + }, + "type": "function" + } + }, + { + "name": "cast_string", + "decl": { + "args": [ + { + "type": "any" + } + ], + "result": { + "type": "string" + }, + "type": "function" + } + }, + { + "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": "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_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": { + "type": "any" + }, + "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": { + "type": "any" + }, + "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" + } + }, + { + "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": { + "type": "any" + }, + "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": { + "type": "any" + }, + "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": "any" + }, + "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": { + "type": "any" + }, + "type": "function" + } + }, + { + "name": "object.union_n", + "decl": { + "args": [ + { + "dynamic": { + "dynamic": { + "key": { + "type": "any" + }, + "value": { + "type": "any" + } + }, + "type": "object" + }, + "type": "array" + } + ], + "result": { + "type": "any" + }, + "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" + } + }, + { + "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" + } + }, + { + "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.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": "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": "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": [ + "contains", + "every", + "if", + "in" + ], + "wasm_abi_versions": [ + { + "version": 1, + "minor_version": 1 + }, + { + "version": 1, + "minor_version": 2 + } + ], + "features": [ + "rule_head_ref_string_prefixes", + "rule_head_refs", + "rego_v1_import" + ] +} diff --git a/vendor/github.com/open-policy-agent/opa/format/format.go b/vendor/github.com/open-policy-agent/opa/format/format.go index 6d327296dd0..02b6f73d823 100644 --- a/vendor/github.com/open-policy-agent/opa/format/format.go +++ b/vendor/github.com/open-policy-agent/opa/format/format.go @@ -1340,7 +1340,10 @@ func closingLoc(skipOpen, skipClose, open, close byte, loc *ast.Location) *ast.L i, offset = skipPast(skipOpen, skipClose, loc) } - for ; i < len(loc.Text) && loc.Text[i] != open; i++ { + for ; i < len(loc.Text); i++ { + if loc.Text[i] == open { + break + } } if i >= len(loc.Text) { @@ -1369,7 +1372,10 @@ func closingLoc(skipOpen, skipClose, open, close byte, loc *ast.Location) *ast.L func skipPast(open, close byte, loc *ast.Location) (int, int) { i := 0 - for ; i < len(loc.Text) && loc.Text[i] != open; i++ { + for ; i < len(loc.Text); i++ { + if loc.Text[i] == open { + break + } } state := 1 diff --git a/vendor/github.com/open-policy-agent/opa/internal/bundle/utils.go b/vendor/github.com/open-policy-agent/opa/internal/bundle/utils.go index 89f3e2a6f46..064649733a4 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/bundle/utils.go +++ b/vendor/github.com/open-policy-agent/opa/internal/bundle/utils.go @@ -95,7 +95,8 @@ func LoadBundleFromDisk(path, name string, bvc *bundle.VerificationConfig) (*bun func LoadBundleFromDiskForRegoVersion(regoVersion ast.RegoVersion, path, name string, bvc *bundle.VerificationConfig) (*bundle.Bundle, error) { bundlePath := filepath.Join(path, name, "bundle.tar.gz") - if _, err := os.Stat(bundlePath); err == nil { + _, err := os.Stat(bundlePath) + if err == nil { f, err := os.Open(filepath.Join(bundlePath)) if err != nil { return nil, err @@ -116,9 +117,9 @@ func LoadBundleFromDiskForRegoVersion(regoVersion ast.RegoVersion, path, name st return &b, nil } else if os.IsNotExist(err) { return nil, nil - } else { - return nil, err } + + return nil, err } // SaveBundleToDisk saves the given raw bytes representing the bundle's content to disk diff --git a/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/wasm.go b/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/wasm.go index 3984d8662fa..9a5cebec542 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/wasm.go +++ b/vendor/github.com/open-policy-agent/opa/internal/compiler/wasm/wasm.go @@ -1139,6 +1139,17 @@ func (c *Compiler) compileBlock(block *ir.Block) ([]instruction.Instruction, err instrs = append(instrs, instruction.Br{Index: 0}) break } + case *ir.IsSetStmt: + if loc, ok := stmt.Source.Value.(ir.Local); ok { + instrs = append(instrs, instruction.GetLocal{Index: c.local(loc)}) + instrs = append(instrs, instruction.Call{Index: c.function(opaValueType)}) + instrs = append(instrs, instruction.I32Const{Value: opaTypeSet}) + instrs = append(instrs, instruction.I32Ne{}) + instrs = append(instrs, instruction.BrIf{Index: 0}) + } else { + instrs = append(instrs, instruction.Br{Index: 0}) + break + } case *ir.IsUndefinedStmt: instrs = append(instrs, instruction.GetLocal{Index: c.local(stmt.Source)}) instrs = append(instrs, instruction.I32Const{Value: 0}) @@ -1321,7 +1332,7 @@ func (c *Compiler) compileWithStmt(with *ir.WithStmt, result *[]instruction.Inst return nil } -func (c *Compiler) compileUpsert(local ir.Local, path []int, value ir.Operand, loc ir.Location, instrs []instruction.Instruction) []instruction.Instruction { +func (c *Compiler) compileUpsert(local ir.Local, path []int, value ir.Operand, _ ir.Location, instrs []instruction.Instruction) []instruction.Instruction { lcopy := c.genLocal() // holds copy of local instrs = append(instrs, instruction.GetLocal{Index: c.local(local)}) 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 5bdfada5d90..8e035013c25 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 @@ -788,7 +788,7 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *SubSchema) return nil } -func (d *Schema) parseReference(documentNode interface{}, currentSchema *SubSchema) error { +func (d *Schema) parseReference(_ interface{}, currentSchema *SubSchema) error { var ( refdDocumentNode interface{} dsp *schemaPoolDocument 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 58d10c1f76e..7c86e37245e 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 @@ -556,10 +556,10 @@ func (v *SubSchema) validateArray(currentSubSchema *SubSchema, value []interface if validationResult.Valid() { validatedOne = true break - } else { - if bestValidationResult == nil || validationResult.score > bestValidationResult.score { - bestValidationResult = validationResult - } + } + + if bestValidationResult == nil || validationResult.score > bestValidationResult.score { + bestValidationResult = validationResult } } if !validatedOne { diff --git a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/fragments_on_composite_types.go b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/fragments_on_composite_types.go index 1af9c4f9667..66bd348c478 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/fragments_on_composite_types.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/fragments_on_composite_types.go @@ -25,7 +25,7 @@ func init() { ) }) - observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) { + observers.OnFragment(func(_ *Walker, fragment *ast.FragmentDefinition) { if fragment.Definition == nil || fragment.TypeCondition == "" || fragment.Definition.IsCompositeType() { return } diff --git a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/known_argument_names.go b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/known_argument_names.go index d9aa8873e0a..36b2d057c9e 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/known_argument_names.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/known_argument_names.go @@ -10,7 +10,7 @@ import ( func init() { AddRule("KnownArgumentNames", func(observers *Events, addError AddErrFunc) { // A GraphQL field is only valid if all supplied arguments are defined by that field. - observers.OnField(func(walker *Walker, field *ast.Field) { + observers.OnField(func(_ *Walker, field *ast.Field) { if field.Definition == nil || field.ObjectDefinition == nil { return } @@ -33,7 +33,7 @@ func init() { } }) - observers.OnDirective(func(walker *Walker, directive *ast.Directive) { + observers.OnDirective(func(_ *Walker, directive *ast.Directive) { if directive.Definition == nil { return } diff --git a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/known_directives.go b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/known_directives.go index 016541e821a..9855291e3b5 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/known_directives.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/known_directives.go @@ -15,7 +15,7 @@ func init() { Column int } var seen = map[mayNotBeUsedDirective]bool{} - observers.OnDirective(func(walker *Walker, directive *ast.Directive) { + observers.OnDirective(func(_ *Walker, directive *ast.Directive) { if directive.Definition == nil { addError( Message(`Unknown directive "@%s".`, directive.Name), diff --git a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/known_fragment_names.go b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/known_fragment_names.go index 94583dac337..8ae1fc33f42 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/known_fragment_names.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/known_fragment_names.go @@ -9,7 +9,7 @@ import ( func init() { AddRule("KnownFragmentNames", func(observers *Events, addError AddErrFunc) { - observers.OnFragmentSpread(func(walker *Walker, fragmentSpread *ast.FragmentSpread) { + observers.OnFragmentSpread(func(_ *Walker, fragmentSpread *ast.FragmentSpread) { if fragmentSpread.Definition == nil { addError( Message(`Unknown fragment "%s".`, fragmentSpread.Name), diff --git a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/no_unused_fragments.go b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/no_unused_fragments.go index f82cfe9f9cf..f6ba046a1cb 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/no_unused_fragments.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/no_unused_fragments.go @@ -13,13 +13,13 @@ func init() { inFragmentDefinition := false fragmentNameUsed := make(map[string]bool) - observers.OnFragmentSpread(func(walker *Walker, fragmentSpread *ast.FragmentSpread) { + observers.OnFragmentSpread(func(_ *Walker, fragmentSpread *ast.FragmentSpread) { if !inFragmentDefinition { fragmentNameUsed[fragmentSpread.Name] = true } }) - observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) { + observers.OnFragment(func(_ *Walker, fragment *ast.FragmentDefinition) { inFragmentDefinition = true if !fragmentNameUsed[fragment.Name] { addError( diff --git a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/no_unused_variables.go b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/no_unused_variables.go index c019ae42e0d..163ac895b51 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/no_unused_variables.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/no_unused_variables.go @@ -9,7 +9,7 @@ import ( func init() { AddRule("NoUnusedVariables", func(observers *Events, addError AddErrFunc) { - observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { + observers.OnOperation(func(_ *Walker, operation *ast.OperationDefinition) { for _, varDef := range operation.VariableDefinitions { if varDef.Used { continue diff --git a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/provided_required_arguments.go b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/provided_required_arguments.go index 20364f8ba9f..d6d12c4fd21 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/provided_required_arguments.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/provided_required_arguments.go @@ -9,7 +9,7 @@ import ( func init() { AddRule("ProvidedRequiredArguments", func(observers *Events, addError AddErrFunc) { - observers.OnField(func(walker *Walker, field *ast.Field) { + observers.OnField(func(_ *Walker, field *ast.Field) { if field.Definition == nil { return } @@ -35,7 +35,7 @@ func init() { } }) - observers.OnDirective(func(walker *Walker, directive *ast.Directive) { + observers.OnDirective(func(_ *Walker, directive *ast.Directive) { if directive.Definition == nil { return } diff --git a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_argument_names.go b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_argument_names.go index 4466d567202..7458c5f6cba 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_argument_names.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_argument_names.go @@ -9,11 +9,11 @@ import ( func init() { AddRule("UniqueArgumentNames", func(observers *Events, addError AddErrFunc) { - observers.OnField(func(walker *Walker, field *ast.Field) { + observers.OnField(func(_ *Walker, field *ast.Field) { checkUniqueArgs(field.Arguments, addError) }) - observers.OnDirective(func(walker *Walker, directive *ast.Directive) { + observers.OnDirective(func(_ *Walker, directive *ast.Directive) { checkUniqueArgs(directive.Arguments, addError) }) }) diff --git a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_directives_per_location.go b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_directives_per_location.go index 09968a74168..ecf5a0a82e2 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_directives_per_location.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_directives_per_location.go @@ -9,7 +9,7 @@ import ( func init() { AddRule("UniqueDirectivesPerLocation", func(observers *Events, addError AddErrFunc) { - observers.OnDirectiveList(func(walker *Walker, directives []*ast.Directive) { + observers.OnDirectiveList(func(_ *Walker, directives []*ast.Directive) { seen := map[string]bool{} for _, dir := range directives { diff --git a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_fragment_names.go b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_fragment_names.go index 3da46467e84..c94f3ad27cb 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_fragment_names.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_fragment_names.go @@ -11,7 +11,7 @@ func init() { AddRule("UniqueFragmentNames", func(observers *Events, addError AddErrFunc) { seenFragments := map[string]bool{} - observers.OnFragment(func(walker *Walker, fragment *ast.FragmentDefinition) { + observers.OnFragment(func(_ *Walker, fragment *ast.FragmentDefinition) { if seenFragments[fragment.Name] { addError( Message(`There can be only one fragment named "%s".`, fragment.Name), diff --git a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_input_field_names.go b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_input_field_names.go index f9f69051bf1..a93d63bd1e8 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_input_field_names.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_input_field_names.go @@ -9,7 +9,7 @@ import ( func init() { AddRule("UniqueInputFieldNames", func(observers *Events, addError AddErrFunc) { - observers.OnValue(func(walker *Walker, value *ast.Value) { + observers.OnValue(func(_ *Walker, value *ast.Value) { if value.Kind != ast.ObjectValue { return } diff --git a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_operation_names.go b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_operation_names.go index dc937795bd5..dcd404dadf5 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_operation_names.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_operation_names.go @@ -11,7 +11,7 @@ func init() { AddRule("UniqueOperationNames", func(observers *Events, addError AddErrFunc) { seen := map[string]bool{} - observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { + observers.OnOperation(func(_ *Walker, operation *ast.OperationDefinition) { if seen[operation.Name] { addError( Message(`There can be only one operation named "%s".`, operation.Name), diff --git a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_variable_names.go b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_variable_names.go index 94dab3cf13c..7a214dbe4c4 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_variable_names.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/unique_variable_names.go @@ -9,7 +9,7 @@ import ( func init() { AddRule("UniqueVariableNames", func(observers *Events, addError AddErrFunc) { - observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { + observers.OnOperation(func(_ *Walker, operation *ast.OperationDefinition) { seen := map[string]int{} for _, def := range operation.VariableDefinitions { // add the same error only once per a variable. diff --git a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/values_of_correct_type.go b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/values_of_correct_type.go index 88d8f53c454..8858023d4e9 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/values_of_correct_type.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/values_of_correct_type.go @@ -13,7 +13,7 @@ import ( func init() { AddRule("ValuesOfCorrectType", func(observers *Events, addError AddErrFunc) { - observers.OnValue(func(walker *Walker, value *ast.Value) { + observers.OnValue(func(_ *Walker, value *ast.Value) { if value.Definition == nil || value.ExpectedType == nil { return } diff --git a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/variables_are_input_types.go b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/variables_are_input_types.go index 51cb77ca312..ea4dfcc5ab3 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/variables_are_input_types.go +++ b/vendor/github.com/open-policy-agent/opa/internal/gqlparser/validator/rules/variables_are_input_types.go @@ -9,7 +9,7 @@ import ( func init() { AddRule("VariablesAreInputTypes", func(observers *Events, addError AddErrFunc) { - observers.OnOperation(func(walker *Walker, operation *ast.OperationDefinition) { + observers.OnOperation(func(_ *Walker, operation *ast.OperationDefinition) { for _, def := range operation.VariableDefinitions { if def.Definition == nil { continue 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 a6405291e72..b75d26ddab3 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 @@ -893,6 +893,40 @@ func (p *Planner) planExprEvery(e *ast.Expr, iter planiter) error { }) err := p.planTerm(every.Domain, func() error { + // Assert that the domain is a collection type: + // block outer + // block a + // isArray + // br 1: break outer, and continue + // block b + // isObject + // br 1: break outer, and continue + // block c + // isSet + // br 1: break outer, and continue + // br 1: invalid domain, break every + + aBlock := &ir.Block{} + p.appendStmtToBlock(&ir.IsArrayStmt{Source: p.ltarget}, aBlock) + p.appendStmtToBlock(&ir.BreakStmt{Index: 1}, aBlock) + + bBlock := &ir.Block{} + p.appendStmtToBlock(&ir.IsObjectStmt{Source: p.ltarget}, bBlock) + p.appendStmtToBlock(&ir.BreakStmt{Index: 1}, bBlock) + + cBlock := &ir.Block{} + p.appendStmtToBlock(&ir.IsSetStmt{Source: p.ltarget}, cBlock) + p.appendStmtToBlock(&ir.BreakStmt{Index: 1}, cBlock) + + outerBlock := &ir.BlockStmt{Blocks: []*ir.Block{ + { + Stmts: []ir.Stmt{ + &ir.BlockStmt{Blocks: []*ir.Block{aBlock, bBlock, cBlock}}, + &ir.BreakStmt{Index: 1}}, + }, + }} + p.appendStmt(outerBlock) + return p.planScan(every.Key, func(ir.Local) error { p.appendStmt(&ir.ResetLocalStmt{ Target: cond1, @@ -1489,7 +1523,7 @@ func (p *Planner) planValue(t ast.Value, loc *ast.Location, iter planiter) error } } -func (p *Planner) planNull(null ast.Null, iter planiter) error { +func (p *Planner) planNull(_ ast.Null, iter planiter) error { target := p.newLocal() 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 12bc38ec86b..929f2006e74 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 @@ -174,7 +174,7 @@ type httpSigner struct { PayloadHash string } -func (s *httpSigner) setRequiredSigningFields(headers http.Header, query url.Values) { +func (s *httpSigner) setRequiredSigningFields(headers http.Header, _ url.Values) { amzDate := s.Time.Format(timeFormat) headers.Set(AmzRegionSetKey, strings.Join(s.RegionSet, ",")) 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 e1e8a949f50..e033da74606 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 @@ -8,7 +8,7 @@ import ( "github.com/open-policy-agent/opa/logging" ) -// DoRequestWithClient is a convenience function to get the body of an http response with +// DoRequestWithClient is a convenience function to get the body of an HTTP response with // appropriate error-handling boilerplate and logging. func DoRequestWithClient(req *http.Request, client *http.Client, desc string, logger logging.Logger) ([]byte, error) { resp, err := client.Do(req) @@ -24,22 +24,16 @@ func DoRequestWithClient(req *http.Request, client *http.Client, desc string, lo "headers": resp.Header, }).Debug("Received response from " + desc + " service.") - if resp.StatusCode != 200 { - if logger.GetLevel() == logging.Debug { - body, err := io.ReadAll(resp.Body) - if err == nil { - logger.Debug("Error response with response body: %s", body) - } - } - // could be 404 for role that's not available, but cover all the bases - return nil, errors.New(desc + " HTTP request returned unexpected status: " + resp.Status) - } - body, err := io.ReadAll(resp.Body) if err != nil { // deal with problems reading the body, whatever those might be return nil, errors.New(desc + " HTTP response body could not be read: " + err.Error()) } + if resp.StatusCode != 200 { + logger.Debug("Error response with response body: %s", body) + // could be 404 for role that's not available, but cover all the bases + return nil, errors.New(desc + " HTTP request returned unexpected status: " + resp.Status) + } return body, nil } diff --git a/vendor/github.com/open-policy-agent/opa/internal/providers/aws/v4/util.go b/vendor/github.com/open-policy-agent/opa/internal/providers/aws/v4/util.go index 0cb9cffaf51..c8e7fb1bab0 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/providers/aws/v4/util.go +++ b/vendor/github.com/open-policy-agent/opa/internal/providers/aws/v4/util.go @@ -11,14 +11,9 @@ const doubleSpace = " " // contain multiple side-by-side spaces. func StripExcessSpaces(str string) string { var j, k, l, m, spaces int - // Trim trailing spaces - for j = len(str) - 1; j >= 0 && str[j] == ' '; j-- { - } - // Trim leading spaces - for k = 0; k < j && str[k] == ' '; k++ { - } - str = str[k : j+1] + // Trim leading and trailing spaces + str = strings.Trim(str, " ") // Strip multiple spaces. j = strings.Index(str, doubleSpace) diff --git a/vendor/github.com/open-policy-agent/opa/internal/strings/strings.go b/vendor/github.com/open-policy-agent/opa/internal/strings/strings.go index 76fb9ee8c84..08f3bf9182e 100644 --- a/vendor/github.com/open-policy-agent/opa/internal/strings/strings.go +++ b/vendor/github.com/open-policy-agent/opa/internal/strings/strings.go @@ -63,6 +63,14 @@ func TruncateFilePaths(maxIdealWidth, maxWidth int, path ...string) (map[string] return result, longestLocation } +func Truncate(str string, maxWidth int) string { + if len(str) <= maxWidth { + return str + } + + return str[:maxWidth-3] + "..." +} + func getPathFromFirstSeparator(path string) string { s := filepath.Dir(path) s = strings.TrimPrefix(s, string(filepath.Separator)) 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 d9c1bda3da5..35e6059c72e 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 @@ -375,10 +375,10 @@ func readTableSection(r io.Reader, s *module.TableSection) error { return err } else if elem != constant.ElementTypeAnyFunc { return fmt.Errorf("illegal element type") - } else { - table.Type = types.Anyfunc } + table.Type = types.Anyfunc + if err := readLimits(r, &table.Lim); err != nil { return err } diff --git a/vendor/github.com/open-policy-agent/opa/ir/ir.go b/vendor/github.com/open-policy-agent/opa/ir/ir.go index d98b96e2c81..c07670704e4 100644 --- a/vendor/github.com/open-policy-agent/opa/ir/ir.go +++ b/vendor/github.com/open-policy-agent/opa/ir/ir.go @@ -364,6 +364,13 @@ type IsObjectStmt struct { Location } +// IsSetStmt represents a dynamic type check on a local variable. +type IsSetStmt struct { + Source Operand `json:"source"` + + Location +} + // IsDefinedStmt represents a check of whether a local variable is defined. type IsDefinedStmt struct { Source Local `json:"source"` diff --git a/vendor/github.com/open-policy-agent/opa/ir/pretty.go b/vendor/github.com/open-policy-agent/opa/ir/pretty.go index a0f2df3bc5e..6102c5a9117 100644 --- a/vendor/github.com/open-policy-agent/opa/ir/pretty.go +++ b/vendor/github.com/open-policy-agent/opa/ir/pretty.go @@ -25,11 +25,11 @@ type prettyPrinter struct { w io.Writer } -func (pp *prettyPrinter) Before(x interface{}) { +func (pp *prettyPrinter) Before(_ interface{}) { pp.depth++ } -func (pp *prettyPrinter) After(x interface{}) { +func (pp *prettyPrinter) After(_ interface{}) { pp.depth-- } diff --git a/vendor/github.com/open-policy-agent/opa/loader/errors.go b/vendor/github.com/open-policy-agent/opa/loader/errors.go index d6da40a0625..b8aafb14210 100644 --- a/vendor/github.com/open-policy-agent/opa/loader/errors.go +++ b/vendor/github.com/open-policy-agent/opa/loader/errors.go @@ -41,7 +41,7 @@ func (e *Errors) add(err error) { type unsupportedDocumentType string func (path unsupportedDocumentType) Error() string { - return string(path) + ": bad document type" + return string(path) + ": document must be of type object" } type unrecognizedFile string diff --git a/vendor/github.com/open-policy-agent/opa/loader/loader.go b/vendor/github.com/open-policy-agent/opa/loader/loader.go index 1ce0e7868ff..e584bab3cb1 100644 --- a/vendor/github.com/open-policy-agent/opa/loader/loader.go +++ b/vendor/github.com/open-policy-agent/opa/loader/loader.go @@ -81,7 +81,7 @@ type Filter = filter.LoaderFilter // GlobExcludeName excludes files and directories whose names do not match the // shell style pattern at minDepth or greater. func GlobExcludeName(pattern string, minDepth int) Filter { - return func(abspath string, info fs.FileInfo, depth int) bool { + return func(_ string, info fs.FileInfo, depth int) bool { match, _ := filepath.Match(pattern, info.Name()) return match && depth >= minDepth } @@ -486,7 +486,7 @@ func AsBundle(path string) (*bundle.Bundle, error) { // AllRegos returns a Result object loaded (recursively) with all Rego source // files from the specified paths. func AllRegos(paths []string) (*Result, error) { - return NewFileLoader().Filtered(paths, func(_ string, info os.FileInfo, depth int) bool { + return NewFileLoader().Filtered(paths, func(_ string, info os.FileInfo, _ int) bool { return !info.IsDir() && !strings.HasSuffix(info.Name(), bundle.RegoExt) }) } @@ -522,7 +522,7 @@ func Paths(path string, recurse bool) (paths []string, err error) { if err != nil { return nil, err } - err = filepath.Walk(path, func(f string, info os.FileInfo, err error) error { + err = filepath.Walk(path, func(f string, _ os.FileInfo, _ error) error { if !recurse { if path != f && path != filepath.Dir(f) { return filepath.SkipDir diff --git a/vendor/github.com/open-policy-agent/opa/logging/logging.go b/vendor/github.com/open-policy-agent/opa/logging/logging.go index 22eb16e43e0..83e2bcbca5f 100644 --- a/vendor/github.com/open-policy-agent/opa/logging/logging.go +++ b/vendor/github.com/open-policy-agent/opa/logging/logging.go @@ -3,6 +3,7 @@ package logging import ( "context" "io" + "net/http" "github.com/sirupsen/logrus" ) @@ -210,10 +211,15 @@ const reqCtxKey = requestContextKey("request-context-key") // RequestContext represents the request context used to store data // related to the request that could be used on logs. type RequestContext struct { - ClientAddr string - ReqID uint64 - ReqMethod string - ReqPath string + ClientAddr string + ReqID uint64 + ReqMethod string + ReqPath string + HTTPRequestContext HTTPRequestContext +} + +type HTTPRequestContext struct { + Header http.Header } // Fields adapts the RequestContext fields to logrus.Fields. diff --git a/vendor/github.com/open-policy-agent/opa/plugins/plugins.go b/vendor/github.com/open-policy-agent/opa/plugins/plugins.go index 08593059520..bacdd150768 100644 --- a/vendor/github.com/open-policy-agent/opa/plugins/plugins.go +++ b/vendor/github.com/open-policy-agent/opa/plugins/plugins.go @@ -286,11 +286,10 @@ func ValidateAndInjectDefaultsForTriggerMode(a, b *TriggerMode) (*TriggerMode, e return nil, err } return a, nil - - } else { - t := DefaultTriggerMode - return &t, nil } + + t := DefaultTriggerMode + return &t, nil } type namedplugin struct { diff --git a/vendor/github.com/open-policy-agent/opa/plugins/rest/auth.go b/vendor/github.com/open-policy-agent/opa/plugins/rest/auth.go index a28f21782a5..c656817339b 100644 --- a/vendor/github.com/open-policy-agent/opa/plugins/rest/auth.go +++ b/vendor/github.com/open-policy-agent/opa/plugins/rest/auth.go @@ -294,16 +294,13 @@ type oauth2Token struct { ExpiresAt time.Time } -func (ap *oauth2ClientCredentialsAuthPlugin) createAuthJWT(ctx context.Context, claims map[string]interface{}, signingKey interface{}) (*string, error) { +func (ap *oauth2ClientCredentialsAuthPlugin) createAuthJWT(ctx context.Context, extClaims map[string]interface{}, signingKey interface{}) (*string, error) { now := time.Now() - baseClaims := map[string]interface{}{ + claims := map[string]interface{}{ "iat": now.Unix(), "exp": now.Add(10 * time.Minute).Unix(), } - if claims == nil { - claims = make(map[string]interface{}) - } - for k, v := range baseClaims { + for k, v := range extClaims { claims[k] = v } @@ -693,7 +690,7 @@ func (ap *clientTLSAuthPlugin) NewClient(c Config) (*http.Client, error) { return client, nil } -func (ap *clientTLSAuthPlugin) Prepare(req *http.Request) error { +func (ap *clientTLSAuthPlugin) Prepare(_ *http.Request) error { return nil } @@ -708,6 +705,7 @@ type awsSigningAuthPlugin struct { AWSService string `json:"service,omitempty"` AWSSignatureVersion string `json:"signature_version,omitempty"` + host string ecrAuthPlugin *ecrAuthPlugin kmsSignPlugin *awsKMSSignPlugin @@ -827,6 +825,13 @@ func (ap *awsSigningAuthPlugin) NewClient(c Config) (*http.Client, error) { return nil, err } + url, err := url.Parse(c.URL) + if err != nil { + return nil, err + } + + ap.host = url.Host + if ap.logger == nil { ap.logger = c.logger } @@ -839,6 +844,13 @@ func (ap *awsSigningAuthPlugin) NewClient(c Config) (*http.Client, error) { } func (ap *awsSigningAuthPlugin) Prepare(req *http.Request) error { + if ap.host != req.URL.Host { + // Return early if the host does not match. + // This can happen when the OCI registry responded with a redirect to another host. + // For instance, ECR redirects to S3 and the ECR auth header should not be included in the S3 request. + return nil + } + switch ap.AWSService { case "ecr": return ap.ecrAuthPlugin.Prepare(req) diff --git a/vendor/github.com/open-policy-agent/opa/rego/rego.go b/vendor/github.com/open-policy-agent/opa/rego/rego.go index cbe2d0c7da0..266e6d6ab00 100644 --- a/vendor/github.com/open-policy-agent/opa/rego/rego.go +++ b/vendor/github.com/open-policy-agent/opa/rego/rego.go @@ -1496,7 +1496,7 @@ func (r *Rego) Compile(ctx context.Context, opts ...CompileOption) (*CompileResu return r.compileWasm(modules, queries, compileQueryType) // TODO(sr) control flow is funky here } -func (r *Rego) compileWasm(modules []*ast.Module, queries []ast.Body, qType queryType) (*CompileResult, error) { +func (r *Rego) compileWasm(_ []*ast.Module, queries []ast.Body, qType queryType) (*CompileResult, error) { policy, err := r.planQuery(queries, qType) if err != nil { return nil, err @@ -1871,7 +1871,7 @@ func (r *Rego) loadFiles(ctx context.Context, txn storage.Transaction, m metrics return nil } -func (r *Rego) loadBundles(ctx context.Context, txn storage.Transaction, m metrics.Metrics) error { +func (r *Rego) loadBundles(_ context.Context, _ storage.Transaction, m metrics.Metrics) error { if len(r.bundlePaths) == 0 { return nil } @@ -2035,7 +2035,7 @@ func (r *Rego) prepareImports() ([]*ast.Import, error) { return imports, nil } -func (r *Rego) compileQuery(query ast.Body, imports []*ast.Import, m metrics.Metrics, extras []extraStage) (ast.QueryCompiler, ast.Body, error) { +func (r *Rego) compileQuery(query ast.Body, imports []*ast.Import, _ metrics.Metrics, extras []extraStage) (ast.QueryCompiler, ast.Body, error) { var pkg *ast.Package if r.pkg != "" { @@ -2476,7 +2476,7 @@ func (r *Rego) partial(ctx context.Context, ectx *EvalContext) (*PartialQueries, return pq, nil } -func (r *Rego) rewriteQueryToCaptureValue(qc ast.QueryCompiler, query ast.Body) (ast.Body, error) { +func (r *Rego) rewriteQueryToCaptureValue(_ ast.QueryCompiler, query ast.Body) (ast.Body, error) { checkCapture := iteration(query) || len(query) > 1 @@ -2593,7 +2593,7 @@ type transactionCloser func(ctx context.Context, err error) error // regardless of status. func (r *Rego) getTxn(ctx context.Context) (storage.Transaction, transactionCloser, error) { - noopCloser := func(ctx context.Context, err error) error { + noopCloser := func(_ context.Context, _ error) error { return nil // no-op default } diff --git a/vendor/github.com/open-policy-agent/opa/topdown/bindings.go b/vendor/github.com/open-policy-agent/opa/topdown/bindings.go index 7621dac5291..30a8ac5ec4d 100644 --- a/vendor/github.com/open-policy-agent/opa/topdown/bindings.go +++ b/vendor/github.com/open-policy-agent/opa/topdown/bindings.go @@ -43,7 +43,7 @@ func (u *bindings) Iter(caller *bindings, iter func(*ast.Term, *ast.Term) error) var err error - u.values.Iter(func(k *ast.Term, v value) bool { + u.values.Iter(func(k *ast.Term, _ value) bool { if err != nil { return true } diff --git a/vendor/github.com/open-policy-agent/opa/topdown/copypropagation/copypropagation.go b/vendor/github.com/open-policy-agent/opa/topdown/copypropagation/copypropagation.go index 4c6b8c42c78..8824d19bd21 100644 --- a/vendor/github.com/open-policy-agent/opa/topdown/copypropagation/copypropagation.go +++ b/vendor/github.com/open-policy-agent/opa/topdown/copypropagation/copypropagation.go @@ -222,11 +222,11 @@ func (p *CopyPropagator) plugBindings(pctx *plugContext, expr *ast.Expr) *ast.Ex // errors unreachable. x, err := ast.Transform(xform, expr.Copy()) - if expr, ok := x.(*ast.Expr); !ok || err != nil { + expr, ok := x.(*ast.Expr) + if !ok || err != nil { panic("unreachable") - } else { - return expr } + return expr } type bindingPlugTransform struct { diff --git a/vendor/github.com/open-policy-agent/opa/topdown/eval.go b/vendor/github.com/open-policy-agent/opa/topdown/eval.go index 9e93bbc09ec..6263efba645 100644 --- a/vendor/github.com/open-policy-agent/opa/topdown/eval.go +++ b/vendor/github.com/open-policy-agent/opa/topdown/eval.go @@ -237,6 +237,10 @@ func (e *eval) traceWasm(x ast.Node, target *ast.Ref) { e.traceEvent(WasmOp, x, "", target) } +func (e *eval) traceUnify(a, b *ast.Term) { + e.traceEvent(UnifyOp, ast.Equality.Expr(a, b), "", nil) +} + func (e *eval) traceEvent(op Op, x ast.Node, msg string, target *ast.Ref) { if !e.traceEnabled { @@ -275,6 +279,7 @@ func (e *eval) traceEvent(op Op, x ast.Node, msg string, target *ast.Ref) { evt.Locals = ast.NewValueMap() evt.LocalMetadata = map[ast.Var]VarMetadata{} + evt.localVirtualCacheSnapshot = ast.NewValueMap() _ = e.bindings.Iter(nil, func(k, v *ast.Term) error { original := k.Value.(ast.Var) @@ -290,15 +295,21 @@ func (e *eval) traceEvent(op Op, x ast.Node, msg string, target *ast.Ref) { }) // cannot return error ast.WalkTerms(x, func(term *ast.Term) bool { - if v, ok := term.Value.(ast.Var); ok { - if _, ok := evt.LocalMetadata[v]; !ok { - if rewritten, ok := e.rewrittenVar(v); ok { - evt.LocalMetadata[v] = VarMetadata{ + switch x := term.Value.(type) { + case ast.Var: + if _, ok := evt.LocalMetadata[x]; !ok { + if rewritten, ok := e.rewrittenVar(x); ok { + evt.LocalMetadata[x] = VarMetadata{ Name: rewritten, Location: term.Loc(), } } } + case ast.Ref: + groundRef := x.GroundPrefix() + if v, _ := e.virtualCache.Get(groundRef); v != nil { + evt.localVirtualCacheSnapshot.Put(groundRef, v.Value) + } } return false }) @@ -407,15 +418,9 @@ func (e *eval) evalStep(iter evalIterator) error { }) case *ast.Every: eval := evalEvery{ - e: e, - expr: expr, - generator: ast.NewBody( - ast.Equality.Expr( - ast.RefTerm(terms.Domain, terms.Key).SetLocation(terms.Domain.Location), - terms.Value, - ).SetLocation(terms.Domain.Location), - ), - body: terms.Body, + Every: terms, + e: e, + expr: expr, } err = eval.eval(func() error { defined = true @@ -864,7 +869,7 @@ func (e *eval) biunify(a, b *ast.Term, b1, b2 *bindings, iter unifyIterator) err a, b1 = b1.apply(a) b, b2 = b2.apply(b) if e.traceEnabled { - e.traceEvent(UnifyOp, ast.Equality.Expr(a, b), "", nil) + e.traceUnify(a, b) } switch vA := a.Value.(type) { case ast.Var, ast.Ref, *ast.ArrayComprehension, *ast.SetComprehension, *ast.ObjectComprehension: @@ -1102,10 +1107,10 @@ func (e *eval) biunifyComprehension(a, b *ast.Term, b1, b2 *bindings, swap bool, return err } else if value != nil { return e.biunify(value, b, b1, b2, iter) - } else { - e.instr.counterIncr(evalOpComprehensionCacheMiss) } + e.instr.counterIncr(evalOpComprehensionCacheMiss) + switch a := a.Value.(type) { case *ast.ArrayComprehension: return e.biunifyComprehensionArray(a, b, b1, b2, iter) @@ -2566,7 +2571,7 @@ 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(pos int) error { + 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 { @@ -2654,7 +2659,7 @@ func (e evalVirtualPartial) evalOneRulePostUnify(iter unifyIterator, rule *ast.R err := child.eval(func(child *eval) error { defined = true - return e.e.biunifyRuleHead(e.pos+1, e.ref, rule, e.bindings, child.bindings, func(pos int) error { + return e.e.biunifyRuleHead(e.pos+1, e.ref, rule, e.bindings, child.bindings, func(_ int) error { return e.evalOneRuleContinue(iter, rule, child) }) }) @@ -2730,7 +2735,7 @@ func (e evalVirtualPartial) partialEvalSupport(iter unifyIterator) error { return e.e.saveUnify(term, e.rterm, e.bindings, e.rbindings, iter) } -func (e evalVirtualPartial) partialEvalSupportRule(rule *ast.Rule, path ast.Ref) (bool, error) { +func (e evalVirtualPartial) partialEvalSupportRule(rule *ast.Rule, _ ast.Ref) (bool, error) { child := e.e.child(rule.Body) child.traceEnter(rule) @@ -3390,19 +3395,32 @@ func (e evalTerm) save(iter unifyIterator) error { } type evalEvery struct { - e *eval - expr *ast.Expr - generator ast.Body - body ast.Body + *ast.Every + e *eval + expr *ast.Expr } func (e evalEvery) eval(iter unifyIterator) error { // unknowns in domain or body: save the expression, PE its body - if e.e.unknown(e.generator, e.e.bindings) || e.e.unknown(e.body, e.e.bindings) { + if e.e.unknown(e.Domain, e.e.bindings) || e.e.unknown(e.Body, e.e.bindings) { return e.save(iter) } - domain := e.e.closure(e.generator) + if pd := e.e.bindings.Plug(e.Domain); pd != nil { + if !isIterableValue(pd.Value) { + e.e.traceFail(e.expr) + return nil + } + } + + generator := ast.NewBody( + ast.Equality.Expr( + ast.RefTerm(e.Domain, e.Key).SetLocation(e.Domain.Location), + e.Value, + ).SetLocation(e.Domain.Location), + ) + + domain := e.e.closure(generator) all := true // all generator evaluations yield one successful body evaluation domain.traceEnter(e.expr) @@ -3413,14 +3431,14 @@ func (e evalEvery) eval(iter unifyIterator) error { // This would do extra work, like iterating needlessly if domain was a large array. return nil } - body := child.closure(e.body) + body := child.closure(e.Body) body.findOne = true - body.traceEnter(e.body) + body.traceEnter(e.Body) done := false err := body.eval(func(*eval) error { - body.traceExit(e.body) + body.traceExit(e.Body) done = true - body.traceRedo(e.body) + body.traceRedo(e.Body) return nil }) if !done { @@ -3446,6 +3464,15 @@ func (e evalEvery) eval(iter unifyIterator) error { return nil } +// isIterableValue returns true if the AST value is an iterable type. +func isIterableValue(x ast.Value) bool { + switch x.(type) { + case *ast.Array, ast.Object, ast.Set: + return true + } + return false +} + func (e *evalEvery) save(iter unifyIterator) error { return e.e.saveExpr(e.plug(e.expr), e.e.bindings, iter) } diff --git a/vendor/github.com/open-policy-agent/opa/topdown/http.go b/vendor/github.com/open-policy-agent/opa/topdown/http.go index 22e6843d4f5..9d01bc14b28 100644 --- a/vendor/github.com/open-policy-agent/opa/topdown/http.go +++ b/vendor/github.com/open-policy-agent/opa/topdown/http.go @@ -68,6 +68,7 @@ var allowedKeyNames = [...]string{ "raise_error", "caching_mode", "max_retry_attempts", + "cache_ignored_headers", } // ref: https://www.rfc-editor.org/rfc/rfc7231#section-6.1 @@ -168,12 +169,17 @@ func getHTTPResponse(bctx BuiltinContext, req ast.Object) (*ast.Term, error) { bctx.Metrics.Timer(httpSendLatencyMetricKey).Start() - reqExecutor, err := newHTTPRequestExecutor(bctx, req) + key, err := getKeyFromRequest(req) if err != nil { return nil, err } + reqExecutor, err := newHTTPRequestExecutor(bctx, req, key) + if err != nil { + return nil, err + } // Check if cache already has a response for this query + // set headers to exclude cache_ignored_headers resp, err := reqExecutor.CheckCache() if err != nil { return nil, err @@ -198,6 +204,43 @@ func getHTTPResponse(bctx BuiltinContext, req ast.Object) (*ast.Term, error) { return ast.NewTerm(resp), nil } +// getKeyFromRequest returns a key to be used for caching HTTP responses +// deletes headers from request object mentioned in cache_ignored_headers +func getKeyFromRequest(req ast.Object) (ast.Object, error) { + // deep copy so changes to key do not reflect in the request object + key := req.Copy() + cacheIgnoredHeadersTerm := req.Get(ast.StringTerm("cache_ignored_headers")) + allHeadersTerm := req.Get(ast.StringTerm("headers")) + // skip because no headers to delete + if cacheIgnoredHeadersTerm == nil || allHeadersTerm == nil { + // need to explicitly set cache_ignored_headers to null + // equivalent requests might have different sets of exclusion lists + key.Insert(ast.StringTerm("cache_ignored_headers"), ast.NullTerm()) + return key, nil + } + var cacheIgnoredHeaders []string + var allHeaders map[string]interface{} + err := ast.As(cacheIgnoredHeadersTerm.Value, &cacheIgnoredHeaders) + if err != nil { + return nil, err + } + err = ast.As(allHeadersTerm.Value, &allHeaders) + if err != nil { + return nil, err + } + for _, header := range cacheIgnoredHeaders { + delete(allHeaders, header) + } + val, err := ast.InterfaceToValue(allHeaders) + if err != nil { + return nil, err + } + key.Insert(ast.StringTerm("headers"), ast.NewTerm(val)) + // remove cache_ignored_headers key + key.Insert(ast.StringTerm("cache_ignored_headers"), ast.NullTerm()) + return key, nil +} + func init() { createAllowedKeys() createCacheableHTTPStatusCodes() @@ -303,7 +346,7 @@ func useSocket(rawURL string, tlsConfig *tls.Config) (bool, string, *http.Transp u.RawQuery = v.Encode() tr := http.DefaultTransport.(*http.Transport).Clone() - tr.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { + tr.DialContext = func(ctx context.Context, _, _ string) (net.Conn, error) { return http.DefaultTransport.(*http.Transport).DialContext(ctx, "unix", socket) } tr.TLSClientConfig = tlsConfig @@ -482,7 +525,7 @@ func createHTTPRequest(bctx BuiltinContext, obj ast.Object) (*http.Request, *htt case "cache", "caching_mode", "force_cache", "force_cache_duration_seconds", "force_json_decode", "force_yaml_decode", - "raise_error", "max_retry_attempts": // no-op + "raise_error", "max_retry_attempts", "cache_ignored_headers": // no-op default: return nil, nil, fmt.Errorf("invalid parameter %q", key) } @@ -729,13 +772,13 @@ func newHTTPSendCache() *httpSendCache { } func valueHash(v util.T) int { - return v.(ast.Value).Hash() + return ast.StringTerm(v.(ast.Value).String()).Hash() } func valueEq(a, b util.T) bool { av := a.(ast.Value) bv := b.(ast.Value) - return av.Compare(bv) == 0 + return av.String() == bv.String() } func (cache *httpSendCache) get(k ast.Value) *httpSendCacheEntry { @@ -1382,20 +1425,21 @@ type httpRequestExecutor interface { // newHTTPRequestExecutor returns a new HTTP request executor that wraps either an inter-query or // intra-query cache implementation -func newHTTPRequestExecutor(bctx BuiltinContext, key ast.Object) (httpRequestExecutor, error) { - useInterQueryCache, forceCacheParams, err := useInterQueryCache(key) +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) } if useInterQueryCache && bctx.InterQueryBuiltinCache != nil { - return newInterQueryCache(bctx, key, forceCacheParams) + return newInterQueryCache(bctx, req, key, forceCacheParams) } - return newIntraQueryCache(bctx, key) + return newIntraQueryCache(bctx, req, key) } type interQueryCache struct { bctx BuiltinContext + req ast.Object key ast.Object httpReq *http.Request httpClient *http.Client @@ -1404,8 +1448,8 @@ type interQueryCache struct { forceCacheParams *forceCacheParams } -func newInterQueryCache(bctx BuiltinContext, key ast.Object, forceCacheParams *forceCacheParams) (*interQueryCache, error) { - return &interQueryCache{bctx: bctx, key: key, forceCacheParams: forceCacheParams}, nil +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 @@ -1464,21 +1508,22 @@ func (c *interQueryCache) InsertErrorIntoCache(err error) { // ExecuteHTTPRequest executes a HTTP request func (c *interQueryCache) ExecuteHTTPRequest() (*http.Response, error) { var err error - c.httpReq, c.httpClient, err = createHTTPRequest(c.bctx, c.key) + c.httpReq, c.httpClient, err = createHTTPRequest(c.bctx, c.req) if err != nil { return nil, handleHTTPSendErr(c.bctx, err) } - return executeHTTPRequest(c.httpReq, c.httpClient, c.key) + return executeHTTPRequest(c.httpReq, c.httpClient, c.req) } type intraQueryCache struct { bctx BuiltinContext + req ast.Object key ast.Object } -func newIntraQueryCache(bctx BuiltinContext, key ast.Object) (*intraQueryCache, error) { - return &intraQueryCache{bctx: bctx, key: key}, nil +func newIntraQueryCache(bctx BuiltinContext, req ast.Object, key ast.Object) (*intraQueryCache, error) { + return &intraQueryCache{bctx: bctx, req: req, key: key}, nil } // CheckCache checks the cache for the value of the key set on this object @@ -1515,11 +1560,11 @@ func (c *intraQueryCache) InsertErrorIntoCache(err error) { // ExecuteHTTPRequest executes a HTTP request func (c *intraQueryCache) ExecuteHTTPRequest() (*http.Response, error) { - httpReq, httpClient, err := createHTTPRequest(c.bctx, c.key) + httpReq, httpClient, err := createHTTPRequest(c.bctx, c.req) if err != nil { return nil, handleHTTPSendErr(c.bctx, err) } - return executeHTTPRequest(httpReq, httpClient, c.key) + return executeHTTPRequest(httpReq, httpClient, c.req) } func useInterQueryCache(req ast.Object) (bool, *forceCacheParams, error) { diff --git a/vendor/github.com/open-policy-agent/opa/topdown/parse_bytes.go b/vendor/github.com/open-policy-agent/opa/topdown/parse_bytes.go index 9d8fe50681c..0cd4bc193a7 100644 --- a/vendor/github.com/open-policy-agent/opa/topdown/parse_bytes.go +++ b/vendor/github.com/open-policy-agent/opa/topdown/parse_bytes.go @@ -45,7 +45,7 @@ var ( errBytesValueIncludesSpaces = parseNumBytesError("spaces not allowed in resource strings") ) -func builtinNumBytes(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { +func builtinNumBytes(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { var m big.Float raw, err := builtins.StringOperand(operands[0].Value, 1) diff --git a/vendor/github.com/open-policy-agent/opa/topdown/providers.go b/vendor/github.com/open-policy-agent/opa/topdown/providers.go index 1affac51c9c..77db917982d 100644 --- a/vendor/github.com/open-policy-agent/opa/topdown/providers.go +++ b/vendor/github.com/open-policy-agent/opa/topdown/providers.go @@ -86,7 +86,7 @@ func validateAWSAuthParameters(o ast.Object) error { return nil } -func builtinAWSSigV4SignReq(ctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { +func builtinAWSSigV4SignReq(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { // Request object. reqObj, err := builtins.ObjectOperand(operands[0].Value, 1) if err != nil { diff --git a/vendor/github.com/open-policy-agent/opa/topdown/reachable.go b/vendor/github.com/open-policy-agent/opa/topdown/reachable.go index 9cb15d51e3d..8d61018e764 100644 --- a/vendor/github.com/open-policy-agent/opa/topdown/reachable.go +++ b/vendor/github.com/open-policy-agent/opa/topdown/reachable.go @@ -31,7 +31,7 @@ func numberOfEdges(collection *ast.Term) int { return 0 } -func builtinReachable(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { +func builtinReachable(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { // Error on wrong types for args. graph, err := builtins.ObjectOperand(operands[0].Value, 1) if err != nil { @@ -109,7 +109,7 @@ func pathBuilder(graph ast.Object, root *ast.Term, path []*ast.Term, edgeRslt as } -func builtinReachablePaths(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { +func builtinReachablePaths(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { var traceResult = ast.NewSet() // Error on wrong types for args. graph, err := builtins.ObjectOperand(operands[0].Value, 1) diff --git a/vendor/github.com/open-policy-agent/opa/topdown/semver.go b/vendor/github.com/open-policy-agent/opa/topdown/semver.go index ab5aed3eec4..7bb7b9c1838 100644 --- a/vendor/github.com/open-policy-agent/opa/topdown/semver.go +++ b/vendor/github.com/open-policy-agent/opa/topdown/semver.go @@ -12,7 +12,7 @@ import ( "github.com/open-policy-agent/opa/topdown/builtins" ) -func builtinSemVerCompare(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { +func builtinSemVerCompare(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { versionStringA, err := builtins.StringOperand(operands[0].Value, 1) if err != nil { return err @@ -37,7 +37,7 @@ func builtinSemVerCompare(bctx BuiltinContext, operands []*ast.Term, iter func(* return iter(ast.IntNumberTerm(result)) } -func builtinSemVerIsValid(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { +func builtinSemVerIsValid(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { versionString, err := builtins.StringOperand(operands[0].Value, 1) if err != nil { return iter(ast.BooleanTerm(false)) diff --git a/vendor/github.com/open-policy-agent/opa/topdown/strings.go b/vendor/github.com/open-policy-agent/opa/topdown/strings.go index d5baa319728..57f8eab9cad 100644 --- a/vendor/github.com/open-policy-agent/opa/topdown/strings.go +++ b/vendor/github.com/open-policy-agent/opa/topdown/strings.go @@ -16,7 +16,7 @@ import ( "github.com/open-policy-agent/opa/topdown/builtins" ) -func builtinAnyPrefixMatch(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { +func builtinAnyPrefixMatch(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { a, b := operands[0].Value, operands[1].Value var strs []string @@ -50,7 +50,7 @@ func builtinAnyPrefixMatch(bctx BuiltinContext, operands []*ast.Term, iter func( return iter(ast.BooleanTerm(anyStartsWithAny(strs, prefixes))) } -func builtinAnySuffixMatch(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { +func builtinAnySuffixMatch(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { a, b := operands[0].Value, operands[1].Value var strsReversed []string @@ -384,12 +384,12 @@ func builtinReplace(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) return err } - new, err := builtins.StringOperand(operands[2].Value, 3) + n, err := builtins.StringOperand(operands[2].Value, 3) if err != nil { return err } - return iter(ast.StringTerm(strings.Replace(string(s), string(old), string(new), -1))) + return iter(ast.StringTerm(strings.Replace(string(s), string(old), string(n), -1))) } func builtinReplaceN(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { diff --git a/vendor/github.com/open-policy-agent/opa/topdown/tokens.go b/vendor/github.com/open-policy-agent/opa/topdown/tokens.go index b69f3f2d24a..4d5a520f267 100644 --- a/vendor/github.com/open-policy-agent/opa/topdown/tokens.go +++ b/vendor/github.com/open-policy-agent/opa/topdown/tokens.go @@ -233,7 +233,7 @@ func builtinJWTVerifyRSA(a ast.Value, b ast.Value, hasher func() hash.Hash, veri } // Implements ES256 JWT signature verification. -func builtinJWTVerifyES256(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { +func builtinJWTVerifyES256(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { result, err := builtinJWTVerify(operands[0].Value, operands[1].Value, sha256.New, verifyES) if err == nil { return iter(ast.NewTerm(result)) @@ -242,7 +242,7 @@ func builtinJWTVerifyES256(bctx BuiltinContext, operands []*ast.Term, iter func( } // Implements ES384 JWT signature verification -func builtinJWTVerifyES384(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { +func builtinJWTVerifyES384(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { result, err := builtinJWTVerify(operands[0].Value, operands[1].Value, sha512.New384, verifyES) if err == nil { return iter(ast.NewTerm(result)) @@ -251,7 +251,7 @@ func builtinJWTVerifyES384(bctx BuiltinContext, operands []*ast.Term, iter func( } // Implements ES512 JWT signature verification -func builtinJWTVerifyES512(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { +func builtinJWTVerifyES512(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { result, err := builtinJWTVerify(operands[0].Value, operands[1].Value, sha512.New, verifyES) if err == nil { return iter(ast.NewTerm(result)) @@ -413,7 +413,7 @@ func builtinJWTVerify(a ast.Value, b ast.Value, hasher func() hash.Hash, verify } // Implements HS256 (secret) JWT signature verification -func builtinJWTVerifyHS256(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { +func builtinJWTVerifyHS256(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { // Decode the JSON Web Token token, err := decodeJWT(operands[0].Value) if err != nil { @@ -442,7 +442,7 @@ func builtinJWTVerifyHS256(bctx BuiltinContext, operands []*ast.Term, iter func( } // Implements HS384 JWT signature verification -func builtinJWTVerifyHS384(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { +func builtinJWTVerifyHS384(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { // Decode the JSON Web Token token, err := decodeJWT(operands[0].Value) if err != nil { @@ -471,7 +471,7 @@ func builtinJWTVerifyHS384(bctx BuiltinContext, operands []*ast.Term, iter func( } // Implements HS512 JWT signature verification -func builtinJWTVerifyHS512(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { +func builtinJWTVerifyHS512(_ BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error { // Decode the JSON Web Token token, err := decodeJWT(operands[0].Value) if err != nil { @@ -793,7 +793,7 @@ func verifyRSAPSS(key interface{}, hash crypto.Hash, digest []byte, signature [] return nil } -func verifyECDSA(key interface{}, hash crypto.Hash, digest []byte, signature []byte) (err error) { +func verifyECDSA(key interface{}, _ crypto.Hash, digest []byte, signature []byte) (err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf("ECDSA signature verification error: %v", r) @@ -1048,10 +1048,9 @@ func builtinJWTDecodeVerify(bctx BuiltinContext, operands []*ast.Term, iter func // Nested JWT, go round again with payload as first argument a = p.Value continue - } else { - // Non-nested JWT (or we've reached the bottom of the nesting). - break } + // Non-nested JWT (or we've reached the bottom of the nesting). + break } payload, err := extractJSONObject(string(p.Value.(ast.String))) if err != nil { diff --git a/vendor/github.com/open-policy-agent/opa/topdown/trace.go b/vendor/github.com/open-policy-agent/opa/topdown/trace.go index 727938d9c6f..e77713821b7 100644 --- a/vendor/github.com/open-policy-agent/opa/topdown/trace.go +++ b/vendor/github.com/open-policy-agent/opa/topdown/trace.go @@ -5,8 +5,10 @@ package topdown import ( + "bytes" "fmt" "io" + "slices" "strings" iStrs "github.com/open-policy-agent/opa/internal/strings" @@ -18,7 +20,9 @@ import ( const ( minLocationWidth = 5 // len("query") maxIdealLocationWidth = 64 - locationPadding = 4 + columnPadding = 4 + maxExprVarWidth = 32 + maxPrettyExprVarWidth = 64 ) // Op defines the types of tracing events. @@ -62,7 +66,8 @@ const ( // UnifyOp is emitted when two terms are unified. Node will be set to an // equality expression with the two terms. This Node will not have location // info. - UnifyOp Op = "Unify" + UnifyOp Op = "Unify" + FailedAssertionOp Op = "FailedAssertion" ) // VarMetadata provides some user facing information about @@ -84,8 +89,9 @@ type Event struct { Message string // Contains message for Note events. Ref *ast.Ref // Identifies the subject ref for the event. Only applies to Index and Wasm operations. - input *ast.Term - bindings *bindings + input *ast.Term + bindings *bindings + localVirtualCacheSnapshot *ast.ValueMap } // HasRule returns true if the Event contains an ast.Rule. @@ -236,31 +242,162 @@ func (b *BufferTracer) Config() TraceConfig { // PrettyTrace pretty prints the trace to the writer. func PrettyTrace(w io.Writer, trace []*Event) { - prettyTraceWith(w, trace, false) + PrettyTraceWithOpts(w, trace, PrettyTraceOptions{}) } // PrettyTraceWithLocation prints the trace to the writer and includes location information func PrettyTraceWithLocation(w io.Writer, trace []*Event) { - prettyTraceWith(w, trace, true) + PrettyTraceWithOpts(w, trace, PrettyTraceOptions{Locations: true}) } -func prettyTraceWith(w io.Writer, trace []*Event, locations bool) { +type PrettyTraceOptions struct { + Locations bool // Include location information + ExprVariables bool // Include variables found in the expression + LocalVariables bool // Include all local variables +} + +type traceRow []string + +func (r *traceRow) add(s string) { + *r = append(*r, s) +} + +type traceTable struct { + rows []traceRow + maxWidths []int +} + +func (t *traceTable) add(row traceRow) { + t.rows = append(t.rows, row) + for i := range row { + if i >= len(t.maxWidths) { + t.maxWidths = append(t.maxWidths, len(row[i])) + } else if len(row[i]) > t.maxWidths[i] { + t.maxWidths[i] = len(row[i]) + } + } +} + +func (t *traceTable) write(w io.Writer, padding int) { + for _, row := range t.rows { + for i, cell := range row { + width := t.maxWidths[i] + padding + if i < len(row)-1 { + _, _ = fmt.Fprintf(w, "%-*s ", width, cell) + } else { + _, _ = fmt.Fprintf(w, "%s", cell) + } + } + _, _ = fmt.Fprintln(w) + } +} + +func PrettyTraceWithOpts(w io.Writer, trace []*Event, opts PrettyTraceOptions) { depths := depths{} - filePathAliases, longest := getShortenedFileNames(trace) + // FIXME: Can we shorten each location as we process each trace event instead of beforehand? + filePathAliases, _ := getShortenedFileNames(trace) - // Always include some padding between the trace and location - locationWidth := longest + locationPadding + table := traceTable{} for _, event := range trace { depth := depths.GetOrSet(event.QueryID, event.ParentID) - if locations { + row := traceRow{} + + if opts.Locations { location := formatLocation(event, filePathAliases) - fmt.Fprintf(w, "%-*s %s\n", locationWidth, location, formatEvent(event, depth)) - } else { - fmt.Fprintln(w, formatEvent(event, depth)) + row.add(location) + } + + row.add(formatEvent(event, depth)) + + if opts.ExprVariables { + vars := exprLocalVars(event) + keys := sortedKeys(vars) + + buf := new(bytes.Buffer) + buf.WriteString("{") + for i, k := range keys { + if i > 0 { + buf.WriteString(", ") + } + _, _ = fmt.Fprintf(buf, "%v: %s", k, iStrs.Truncate(vars.Get(k).String(), maxExprVarWidth)) + } + buf.WriteString("}") + row.add(buf.String()) + } + + if opts.LocalVariables { + if locals := event.Locals; locals != nil { + 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.WriteString("}") + row.add(buf.String()) + } else { + row.add("{}") + } + } + + table.add(row) + } + + table.write(w, columnPadding) +} + +func sortedKeys(vm *ast.ValueMap) []ast.Value { + keys := make([]ast.Value, 0, vm.Len()) + vm.Iter(func(k, _ ast.Value) bool { + keys = append(keys, k) + return false + }) + slices.SortFunc(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 r, ok := term.Value.(ast.Ref); ok { + // fmt.Printf("ref: %v\n", r) + // //return true + //} + 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) + } + } } + return false } + + 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) + return vars + } + + // The local cache snapshot only contains a snapshot for those refs present in the event node, + // so they can all be added to the vars map. + e.localVirtualCacheSnapshot.Iter(func(k, v ast.Value) bool { + vars.Put(k, v) + return false + }) + + ast.WalkTerms(e.Node, findVars) + + return vars } func formatEvent(event *Event, depth int) string { @@ -451,6 +588,310 @@ func rewrite(event *Event) *Event { return &cpy } +type varInfo struct { + VarMetadata + val ast.Value + exprLoc *ast.Location + col int // 0-indexed column +} + +func (v varInfo) Value() string { + if v.val != nil { + return v.val.String() + } + return "undefined" +} + +func (v varInfo) Title() string { + if v.exprLoc != nil && v.exprLoc.Text != nil { + return string(v.exprLoc.Text) + } + return string(v.Name) +} + +func padLocationText(loc *ast.Location) string { + if loc == nil { + return "" + } + + text := string(loc.Text) + + if loc.Col == 0 { + return text + } + + buf := new(bytes.Buffer) + j := 0 + for i := 1; i < loc.Col; i++ { + if len(loc.Tabs) > 0 && j < len(loc.Tabs) && loc.Tabs[j] == i { + buf.WriteString("\t") + j++ + } else { + buf.WriteString(" ") + } + } + + buf.WriteString(text) + return buf.String() +} + +type PrettyEventOpts struct { + PrettyVars bool +} + +func walkTestTerms(x interface{}, f func(*ast.Term) bool) { + var vis *ast.GenericVisitor + vis = ast.NewGenericVisitor(func(x interface{}) bool { + switch x := x.(type) { + case ast.Call: + for _, t := range x[1:] { + vis.Walk(t) + } + return true + case *ast.Expr: + if x.IsCall() { + for _, o := range x.Operands() { + vis.Walk(o) + } + for i := range x.With { + vis.Walk(x.With[i]) + } + return true + } + case *ast.Term: + return f(x) + case *ast.With: + vis.Walk(x.Value) + return true + } + return false + }) + vis.Walk(x) +} + +func PrettyEvent(w io.Writer, e *Event, opts PrettyEventOpts) error { + if !opts.PrettyVars { + _, _ = fmt.Fprintln(w, padLocationText(e.Location)) + return nil + } + + buf := new(bytes.Buffer) + exprVars := map[string]varInfo{} + + findVars := func(unknownAreUndefined bool) func(term *ast.Term) bool { + return func(term *ast.Term) bool { + if term.Location == nil { + return false + } + + switch v := term.Value.(type) { + case *ast.ArrayComprehension, *ast.SetComprehension, *ast.ObjectComprehension: + // we don't report on the internals of a comprehension, as it's already evaluated, and we won't have the local vars. + return true + case ast.Var: + var info *varInfo + if meta, ok := e.LocalMetadata[v]; ok { + info = &varInfo{ + VarMetadata: meta, + val: e.Locals.Get(v), + exprLoc: term.Location, + } + } else if unknownAreUndefined { + info = &varInfo{ + VarMetadata: VarMetadata{Name: v}, + exprLoc: term.Location, + col: term.Location.Col, + } + } + + if info != nil { + if v, exists := exprVars[info.Title()]; !exists || v.val == nil { + if term.Location != nil { + info.col = term.Location.Col + } + exprVars[info.Title()] = *info + } + } + } + return false + } + } + + expr, ok := e.Node.(*ast.Expr) + if !ok || expr == nil { + return nil + } + + base := expr.BaseCogeneratedExpr() + exprText := padLocationText(base.Location) + buf.WriteString(exprText) + + e.localVirtualCacheSnapshot.Iter(func(k, v ast.Value) bool { + var info *varInfo + switch k := k.(type) { + case ast.Ref: + info = &varInfo{ + VarMetadata: VarMetadata{Name: ast.Var(k.String())}, + val: v, + exprLoc: k[0].Location, + col: k[0].Location.Col, + } + case *ast.ArrayComprehension: + info = &varInfo{ + VarMetadata: VarMetadata{Name: ast.Var(k.String())}, + val: v, + exprLoc: k.Term.Location, + col: k.Term.Location.Col, + } + case *ast.SetComprehension: + info = &varInfo{ + VarMetadata: VarMetadata{Name: ast.Var(k.String())}, + val: v, + exprLoc: k.Term.Location, + col: k.Term.Location.Col, + } + case *ast.ObjectComprehension: + info = &varInfo{ + VarMetadata: VarMetadata{Name: ast.Var(k.String())}, + val: v, + exprLoc: k.Key.Location, + col: k.Key.Location.Col, + } + } + + if info != nil { + exprVars[info.Title()] = *info + } + + return false + }) + + // If the expression is negated, we can't confidently assert that vars with unknown values are 'undefined', + // since the compiler might have opted out of the necessary rewrite. + walkTestTerms(expr, findVars(!expr.Negated)) + coExprs := expr.CogeneratedExprs() + for _, coExpr := range coExprs { + // Only the current "co-expr" can have undefined vars, if we don't know the value for a var in any other co-expr, + // it's unknown, not undefined. A var can be unknown if it hasn't been assigned a value yet, because the co-expr + // hasn't been evaluated yet (the fail happened before it). + walkTestTerms(coExpr, findVars(false)) + } + + printPrettyVars(buf, exprVars) + _, _ = fmt.Fprint(w, buf.String()) + return nil +} + +func printPrettyVars(w *bytes.Buffer, exprVars map[string]varInfo) { + containsTabs := false + varRows := make(map[int]interface{}) + for _, info := range exprVars { + if len(info.exprLoc.Tabs) > 0 { + containsTabs = true + } + varRows[info.exprLoc.Row] = nil + } + + 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") + for _, info := range byName { + w.WriteString(fmt.Sprintf("\n%s: %s", info.Title(), iStrs.Truncate(info.Value(), maxPrettyExprVarWidth))) + } + + return + } + + byCol := make([]varInfo, 0, len(exprVars)) + for _, info := range exprVars { + byCol = append(byCol, info) + } + slices.SortFunc(byCol, func(a, b varInfo) int { + // sort first by column, then by reverse row (to present vars in the same order they appear in the expr) + if a.col == b.col { + if a.exprLoc.Row == b.exprLoc.Row { + return strings.Compare(a.Title(), b.Title()) + } + return b.exprLoc.Row - a.exprLoc.Row + } + return a.col - b.col + }) + + if len(byCol) == 0 { + return + } + + w.WriteString("\n") + printArrows(w, byCol, -1) + for i := len(byCol) - 1; i >= 0; i-- { + w.WriteString("\n") + printArrows(w, byCol, i) + } +} + +func printArrows(w *bytes.Buffer, l []varInfo, printValueAt int) { + prevCol := 0 + var slice []varInfo + if printValueAt >= 0 { + slice = l[:printValueAt+1] + } else { + slice = l + } + isFirst := true + for i, info := range slice { + + isLast := i >= len(slice)-1 + col := info.col + + if !isLast && col == l[i+1].col { + // We're sharing the same column with another, subsequent var + continue + } + + spaces := col - 1 + if i > 0 && !isFirst { + spaces = (col - prevCol) - 1 + } + + for j := 0; j < spaces; j++ { + tab := false + for _, t := range info.exprLoc.Tabs { + if t == j+prevCol+1 { + w.WriteString("\t") + tab = true + break + } + } + if !tab { + w.WriteString(" ") + } + } + + 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. + w.WriteString(fmt.Sprintf("%s: %s", info.Title(), valueStr)) + } else { + w.WriteString(valueStr) + } + } else { + w.WriteString("|") + } + prevCol = col + isFirst = false + } +} + func init() { RegisterBuiltinFunc(ast.Trace.Name, builtinTrace) } diff --git a/vendor/github.com/open-policy-agent/opa/types/decode.go b/vendor/github.com/open-policy-agent/opa/types/decode.go index 4e123384a0a..a6bd9ea030b 100644 --- a/vendor/github.com/open-policy-agent/opa/types/decode.go +++ b/vendor/github.com/open-policy-agent/opa/types/decode.go @@ -77,10 +77,10 @@ func Unmarshal(bs []byte) (result Type, err error) { } } case typeAny: - var any rawunion - if err = util.UnmarshalJSON(bs, &any); err == nil { + var union rawunion + if err = util.UnmarshalJSON(bs, &union); err == nil { var of []Type - if of, err = unmarshalSlice(any.Of); err == nil { + if of, err = unmarshalSlice(union.Of); err == nil { result = NewAny(of...) } } diff --git a/vendor/github.com/open-policy-agent/opa/types/types.go b/vendor/github.com/open-policy-agent/opa/types/types.go index a4b87cd5540..2a050927dd0 100644 --- a/vendor/github.com/open-policy-agent/opa/types/types.go +++ b/vendor/github.com/open-policy-agent/opa/types/types.go @@ -938,8 +938,8 @@ func Compare(a, b Type) int { // Contains returns true if a is a superset or equal to b. func Contains(a, b Type) bool { - if any, ok := unwrap(a).(Any); ok { - return any.Contains(b) + if x, ok := unwrap(a).(Any); ok { + return x.Contains(b) } return Compare(a, b) == 0 } @@ -994,8 +994,8 @@ func Select(a Type, x interface{}) Type { if Compare(a.of, tpe) == 0 { return a.of } - if any, ok := a.of.(Any); ok { - if any.Contains(tpe) { + if x, ok := a.of.(Any); ok { + if x.Contains(tpe) { return tpe } } diff --git a/vendor/github.com/open-policy-agent/opa/util/read_gzip_body.go b/vendor/github.com/open-policy-agent/opa/util/read_gzip_body.go new file mode 100644 index 00000000000..2e33cae5fac --- /dev/null +++ b/vendor/github.com/open-policy-agent/opa/util/read_gzip_body.go @@ -0,0 +1,26 @@ +package util + +import ( + "bytes" + "compress/gzip" + "io" + "net/http" + "strings" +) + +// Note(philipc): Originally taken from server/server.go +func ReadMaybeCompressedBody(r *http.Request) (io.ReadCloser, error) { + if strings.Contains(r.Header.Get("Content-Encoding"), "gzip") { + gzReader, err := gzip.NewReader(r.Body) + if err != nil { + return nil, err + } + defer gzReader.Close() + bytesBody, err := io.ReadAll(gzReader) + if err != nil { + return nil, err + } + return io.NopCloser(bytes.NewReader(bytesBody)), err + } + return r.Body, nil +} diff --git a/vendor/github.com/open-policy-agent/opa/version/version.go b/vendor/github.com/open-policy-agent/opa/version/version.go index 8afe7c6e228..841a54d84cb 100644 --- a/vendor/github.com/open-policy-agent/opa/version/version.go +++ b/vendor/github.com/open-policy-agent/opa/version/version.go @@ -11,7 +11,7 @@ import ( ) // Version is the canonical version of OPA. -var Version = "0.64.1" +var Version = "0.66.0" // GoVersion is the version of Go this was built with var GoVersion = runtime.Version() diff --git a/vendor/github.com/opencontainers/image-spec/specs-go/version.go b/vendor/github.com/opencontainers/image-spec/specs-go/version.go index 6d01f6e7175..7069ae44d71 100644 --- a/vendor/github.com/opencontainers/image-spec/specs-go/version.go +++ b/vendor/github.com/opencontainers/image-spec/specs-go/version.go @@ -25,7 +25,7 @@ const ( VersionPatch = 0 // VersionDev indicates development branch. Releases will be empty string. - VersionDev = "-rc.6" + VersionDev = "" ) // Version is the specification version that the package types support. diff --git a/vendor/google.golang.org/grpc/README.md b/vendor/google.golang.org/grpc/README.md index ab0fbb79b86..b572707c623 100644 --- a/vendor/google.golang.org/grpc/README.md +++ b/vendor/google.golang.org/grpc/README.md @@ -10,7 +10,7 @@ RPC framework that puts mobile and HTTP/2 first. For more information see the ## Prerequisites -- **[Go][]**: any one of the **three latest major** [releases][go-releases]. +- **[Go][]**: any one of the **two latest major** [releases][go-releases]. ## Installation diff --git a/vendor/google.golang.org/grpc/balancer/grpclb/grpc_lb_v1/load_balancer.pb.go b/vendor/google.golang.org/grpc/balancer/grpclb/grpc_lb_v1/load_balancer.pb.go index bdf93dbfeff..0adc98866c0 100644 --- a/vendor/google.golang.org/grpc/balancer/grpclb/grpc_lb_v1/load_balancer.pb.go +++ b/vendor/google.golang.org/grpc/balancer/grpclb/grpc_lb_v1/load_balancer.pb.go @@ -19,7 +19,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.33.0 +// protoc-gen-go v1.34.1 // protoc v4.25.2 // source: grpc/lb/v1/load_balancer.proto diff --git a/vendor/google.golang.org/grpc/balancer/grpclb/grpc_lb_v1/load_balancer_grpc.pb.go b/vendor/google.golang.org/grpc/balancer/grpclb/grpc_lb_v1/load_balancer_grpc.pb.go index c57857ac0e1..57a792a7b48 100644 --- a/vendor/google.golang.org/grpc/balancer/grpclb/grpc_lb_v1/load_balancer_grpc.pb.go +++ b/vendor/google.golang.org/grpc/balancer/grpclb/grpc_lb_v1/load_balancer_grpc.pb.go @@ -19,7 +19,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc v4.25.2 // source: grpc/lb/v1/load_balancer.proto diff --git a/vendor/google.golang.org/grpc/balancer/grpclb/grpclb_config.go b/vendor/google.golang.org/grpc/balancer/grpclb/grpclb_config.go index 8942c31310a..96a57c8c70c 100644 --- a/vendor/google.golang.org/grpc/balancer/grpclb/grpclb_config.go +++ b/vendor/google.golang.org/grpc/balancer/grpclb/grpclb_config.go @@ -21,14 +21,14 @@ package grpclb import ( "encoding/json" - "google.golang.org/grpc" + "google.golang.org/grpc/balancer/pickfirst" "google.golang.org/grpc/balancer/roundrobin" "google.golang.org/grpc/serviceconfig" ) const ( roundRobinName = roundrobin.Name - pickFirstName = grpc.PickFirstBalancerName + pickFirstName = pickfirst.Name ) type grpclbServiceConfig struct { diff --git a/vendor/google.golang.org/grpc/balancer/grpclb/grpclb_picker.go b/vendor/google.golang.org/grpc/balancer/grpclb/grpclb_picker.go index 20c5f2ec396..671bc663fcb 100644 --- a/vendor/google.golang.org/grpc/balancer/grpclb/grpclb_picker.go +++ b/vendor/google.golang.org/grpc/balancer/grpclb/grpclb_picker.go @@ -19,13 +19,13 @@ package grpclb import ( + "math/rand" "sync" "sync/atomic" "google.golang.org/grpc/balancer" lbpb "google.golang.org/grpc/balancer/grpclb/grpc_lb_v1" "google.golang.org/grpc/codes" - "google.golang.org/grpc/internal/grpcrand" "google.golang.org/grpc/status" ) @@ -112,7 +112,7 @@ type rrPicker struct { func newRRPicker(readySCs []balancer.SubConn) *rrPicker { return &rrPicker{ subConns: readySCs, - subConnsNext: grpcrand.Intn(len(readySCs)), + subConnsNext: rand.Intn(len(readySCs)), } } @@ -147,7 +147,7 @@ func newLBPicker(serverList []*lbpb.Server, readySCs []balancer.SubConn, stats * return &lbPicker{ serverList: serverList, subConns: readySCs, - subConnsNext: grpcrand.Intn(len(readySCs)), + subConnsNext: rand.Intn(len(readySCs)), stats: stats, } } diff --git a/vendor/google.golang.org/grpc/pickfirst.go b/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go similarity index 89% rename from vendor/google.golang.org/grpc/pickfirst.go rename to vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go index 8853626614e..07527603f1d 100644 --- a/vendor/google.golang.org/grpc/pickfirst.go +++ b/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go @@ -16,26 +16,36 @@ * */ -package grpc +// Package pickfirst contains the pick_first load balancing policy. +package pickfirst import ( "encoding/json" "errors" "fmt" + "math/rand" "google.golang.org/grpc/balancer" "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/internal" internalgrpclog "google.golang.org/grpc/internal/grpclog" - "google.golang.org/grpc/internal/grpcrand" "google.golang.org/grpc/internal/pretty" "google.golang.org/grpc/resolver" "google.golang.org/grpc/serviceconfig" ) +func init() { + balancer.Register(pickfirstBuilder{}) + internal.ShuffleAddressListForTesting = func(n int, swap func(i, j int)) { rand.Shuffle(n, swap) } +} + +var logger = grpclog.Component("pick-first-lb") + const ( - // PickFirstBalancerName is the name of the pick_first balancer. - PickFirstBalancerName = "pick_first" - logPrefix = "[pick-first-lb %p] " + // Name is the name of the pick_first balancer. + Name = "pick_first" + logPrefix = "[pick-first-lb %p] " ) type pickfirstBuilder struct{} @@ -47,7 +57,7 @@ func (pickfirstBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions) } func (pickfirstBuilder) Name() string { - return PickFirstBalancerName + return Name } type pfConfig struct { @@ -93,6 +103,12 @@ func (b *pickfirstBalancer) ResolverError(err error) { }) } +type Shuffler interface { + ShuffleAddressListForTesting(n int, swap func(i, j int)) +} + +func ShuffleAddressListForTesting(n int, swap func(i, j int)) { rand.Shuffle(n, swap) } + func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState) error { if len(state.ResolverState.Addresses) == 0 && len(state.ResolverState.Endpoints) == 0 { // The resolver reported an empty address list. Treat it like an error by @@ -124,7 +140,7 @@ func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState // within each endpoint. - A61 if cfg.ShuffleAddressList { endpoints = append([]resolver.Endpoint{}, endpoints...) - grpcrand.Shuffle(len(endpoints), func(i, j int) { endpoints[i], endpoints[j] = endpoints[j], endpoints[i] }) + internal.ShuffleAddressListForTesting.(func(int, func(int, int)))(len(endpoints), func(i, j int) { endpoints[i], endpoints[j] = endpoints[j], endpoints[i] }) } // "Flatten the list by concatenating the ordered list of addresses for each @@ -145,7 +161,7 @@ func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState addrs = state.ResolverState.Addresses if cfg.ShuffleAddressList { addrs = append([]resolver.Address{}, addrs...) - grpcrand.Shuffle(len(addrs), func(i, j int) { addrs[i], addrs[j] = addrs[j], addrs[i] }) + rand.Shuffle(len(addrs), func(i, j int) { addrs[i], addrs[j] = addrs[j], addrs[i] }) } } diff --git a/vendor/google.golang.org/grpc/balancer/roundrobin/roundrobin.go b/vendor/google.golang.org/grpc/balancer/roundrobin/roundrobin.go index f7031ad2251..260255d31b6 100644 --- a/vendor/google.golang.org/grpc/balancer/roundrobin/roundrobin.go +++ b/vendor/google.golang.org/grpc/balancer/roundrobin/roundrobin.go @@ -22,12 +22,12 @@ package roundrobin import ( + "math/rand" "sync/atomic" "google.golang.org/grpc/balancer" "google.golang.org/grpc/balancer/base" "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/internal/grpcrand" ) // Name is the name of round_robin balancer. @@ -60,7 +60,7 @@ func (*rrPickerBuilder) Build(info base.PickerBuildInfo) balancer.Picker { // Start at a random index, as the same RR balancer rebuilds a new // picker when SubConn states change, and we don't want to apply excess // load to the first server in the list. - next: uint32(grpcrand.Intn(len(scs))), + next: uint32(rand.Intn(len(scs))), } } diff --git a/vendor/google.golang.org/grpc/balancer_wrapper.go b/vendor/google.golang.org/grpc/balancer_wrapper.go index af39b8a4c73..4161fdf47a8 100644 --- a/vendor/google.golang.org/grpc/balancer_wrapper.go +++ b/vendor/google.golang.org/grpc/balancer_wrapper.go @@ -198,6 +198,10 @@ func (ccb *ccBalancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resol func (ccb *ccBalancerWrapper) UpdateState(s balancer.State) { ccb.cc.mu.Lock() defer ccb.cc.mu.Unlock() + if ccb.cc.conns == nil { + // The CC has been closed; ignore this update. + return + } ccb.mu.Lock() if ccb.closed { diff --git a/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go b/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go index 1afb1e84ac0..63c639e4fe9 100644 --- a/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go +++ b/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go @@ -18,7 +18,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.33.0 +// protoc-gen-go v1.34.1 // protoc v4.25.2 // source: grpc/binlog/v1/binarylog.proto diff --git a/vendor/google.golang.org/grpc/clientconn.go b/vendor/google.golang.org/grpc/clientconn.go index 2359f94b8a4..423be7b43b0 100644 --- a/vendor/google.golang.org/grpc/clientconn.go +++ b/vendor/google.golang.org/grpc/clientconn.go @@ -31,6 +31,7 @@ import ( "google.golang.org/grpc/balancer" "google.golang.org/grpc/balancer/base" + "google.golang.org/grpc/balancer/pickfirst" "google.golang.org/grpc/codes" "google.golang.org/grpc/connectivity" "google.golang.org/grpc/internal" @@ -72,6 +73,8 @@ var ( // invalidDefaultServiceConfigErrPrefix is used to prefix the json parsing error for the default // service config. invalidDefaultServiceConfigErrPrefix = "grpc: the provided default service config is invalid" + // PickFirstBalancerName is the name of the pick_first balancer. + PickFirstBalancerName = pickfirst.Name ) // The following errors are returned from Dial and DialContext @@ -152,6 +155,16 @@ func NewClient(target string, opts ...DialOption) (conn *ClientConn, err error) for _, opt := range opts { opt.apply(&cc.dopts) } + + // Determine the resolver to use. + if err := cc.initParsedTargetAndResolverBuilder(); err != nil { + return nil, err + } + + for _, opt := range globalPerTargetDialOptions { + opt.DialOptionForTarget(cc.parsedTarget.URL).apply(&cc.dopts) + } + chainUnaryClientInterceptors(cc) chainStreamClientInterceptors(cc) @@ -160,7 +173,7 @@ func NewClient(target string, opts ...DialOption) (conn *ClientConn, err error) } if cc.dopts.defaultServiceConfigRawJSON != nil { - scpr := parseServiceConfig(*cc.dopts.defaultServiceConfigRawJSON) + scpr := parseServiceConfig(*cc.dopts.defaultServiceConfigRawJSON, cc.dopts.maxCallAttempts) if scpr.Err != nil { return nil, fmt.Errorf("%s: %v", invalidDefaultServiceConfigErrPrefix, scpr.Err) } @@ -168,25 +181,16 @@ func NewClient(target string, opts ...DialOption) (conn *ClientConn, err error) } cc.mkp = cc.dopts.copts.KeepaliveParams - // Register ClientConn with channelz. - cc.channelzRegistration(target) - - // TODO: Ideally it should be impossible to error from this function after - // channelz registration. This will require removing some channelz logs - // from the following functions that can error. Errors can be returned to - // the user, and successful logs can be emitted here, after the checks have - // passed and channelz is subsequently registered. - - // Determine the resolver to use. - if err := cc.parseTargetAndFindResolver(); err != nil { - channelz.RemoveEntry(cc.channelz.ID) - return nil, err - } - if err = cc.determineAuthority(); err != nil { - channelz.RemoveEntry(cc.channelz.ID) + if err = cc.initAuthority(); err != nil { return nil, err } + // Register ClientConn with channelz. Note that this is only done after + // channel creation cannot fail. + cc.channelzRegistration(target) + channelz.Infof(logger, cc.channelz, "parsed dial target is: %#v", cc.parsedTarget) + channelz.Infof(logger, cc.channelz, "Channel authority set to %q", cc.authority) + cc.csMgr = newConnectivityStateManager(cc.ctx, cc.channelz) cc.pickerWrapper = newPickerWrapper(cc.dopts.copts.StatsHandlers) @@ -587,11 +591,11 @@ type ClientConn struct { // The following are initialized at dial time, and are read-only after that. target string // User's dial target. - parsedTarget resolver.Target // See parseTargetAndFindResolver(). - authority string // See determineAuthority(). + parsedTarget resolver.Target // See initParsedTargetAndResolverBuilder(). + authority string // See initAuthority(). dopts dialOptions // Default and user specified dial options. channelz *channelz.Channel // Channelz object. - resolverBuilder resolver.Builder // See parseTargetAndFindResolver(). + resolverBuilder resolver.Builder // See initParsedTargetAndResolverBuilder(). idlenessMgr *idle.Manager // The following provide their own synchronization, and therefore don't @@ -692,8 +696,7 @@ func (cc *ClientConn) waitForResolvedAddrs(ctx context.Context) error { var emptyServiceConfig *ServiceConfig func init() { - balancer.Register(pickfirstBuilder{}) - cfg := parseServiceConfig("{}") + cfg := parseServiceConfig("{}", defaultMaxCallAttempts) if cfg.Err != nil { panic(fmt.Sprintf("impossible error parsing empty service config: %v", cfg.Err)) } @@ -1673,22 +1676,19 @@ func (cc *ClientConn) connectionError() error { return cc.lastConnectionError } -// parseTargetAndFindResolver parses the user's dial target and stores the -// parsed target in `cc.parsedTarget`. +// initParsedTargetAndResolverBuilder parses the user's dial target and stores +// the parsed target in `cc.parsedTarget`. // // The resolver to use is determined based on the scheme in the parsed target // and the same is stored in `cc.resolverBuilder`. // // Doesn't grab cc.mu as this method is expected to be called only at Dial time. -func (cc *ClientConn) parseTargetAndFindResolver() error { - channelz.Infof(logger, cc.channelz, "original dial target is: %q", cc.target) +func (cc *ClientConn) initParsedTargetAndResolverBuilder() error { + logger.Infof("original dial target is: %q", cc.target) var rb resolver.Builder parsedTarget, err := parseTarget(cc.target) - if err != nil { - channelz.Infof(logger, cc.channelz, "dial target %q parse failed: %v", cc.target, err) - } else { - channelz.Infof(logger, cc.channelz, "parsed dial target is: %#v", parsedTarget) + if err == nil { rb = cc.getResolver(parsedTarget.URL.Scheme) if rb != nil { cc.parsedTarget = parsedTarget @@ -1707,15 +1707,12 @@ func (cc *ClientConn) parseTargetAndFindResolver() error { defScheme = resolver.GetDefaultScheme() } - channelz.Infof(logger, cc.channelz, "fallback to scheme %q", defScheme) canonicalTarget := defScheme + ":///" + cc.target parsedTarget, err = parseTarget(canonicalTarget) if err != nil { - channelz.Infof(logger, cc.channelz, "dial target %q parse failed: %v", canonicalTarget, err) return err } - channelz.Infof(logger, cc.channelz, "parsed dial target is: %+v", parsedTarget) rb = cc.getResolver(parsedTarget.URL.Scheme) if rb == nil { return fmt.Errorf("could not get resolver for default scheme: %q", parsedTarget.URL.Scheme) @@ -1805,7 +1802,7 @@ func encodeAuthority(authority string) string { // credentials do not match the authority configured through the dial option. // // Doesn't grab cc.mu as this method is expected to be called only at Dial time. -func (cc *ClientConn) determineAuthority() error { +func (cc *ClientConn) initAuthority() error { dopts := cc.dopts // Historically, we had two options for users to specify the serverName or // authority for a channel. One was through the transport credentials @@ -1838,6 +1835,5 @@ func (cc *ClientConn) determineAuthority() error { } else { cc.authority = encodeAuthority(endpoint) } - channelz.Infof(logger, cc.channelz, "Channel authority set to %q", cc.authority) return nil } diff --git a/vendor/google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp/altscontext.pb.go b/vendor/google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp/altscontext.pb.go index fe4488a95ed..38cb5cf0d74 100644 --- a/vendor/google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp/altscontext.pb.go +++ b/vendor/google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp/altscontext.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.33.0 +// protoc-gen-go v1.34.1 // protoc v4.25.2 // source: grpc/gcp/altscontext.proto diff --git a/vendor/google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp/handshaker.pb.go b/vendor/google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp/handshaker.pb.go index adbad6b2fa3..55fc7f65f10 100644 --- a/vendor/google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp/handshaker.pb.go +++ b/vendor/google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp/handshaker.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.33.0 +// protoc-gen-go v1.34.1 // protoc v4.25.2 // source: grpc/gcp/handshaker.proto diff --git a/vendor/google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp/handshaker_grpc.pb.go b/vendor/google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp/handshaker_grpc.pb.go index d1af55260bd..358074b6494 100644 --- a/vendor/google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp/handshaker_grpc.pb.go +++ b/vendor/google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp/handshaker_grpc.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc v4.25.2 // source: grpc/gcp/handshaker.proto diff --git a/vendor/google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp/transport_security_common.pb.go b/vendor/google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp/transport_security_common.pb.go index d65ffe6e7be..18cc9cfbd59 100644 --- a/vendor/google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp/transport_security_common.pb.go +++ b/vendor/google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp/transport_security_common.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.33.0 +// protoc-gen-go v1.34.1 // protoc v4.25.2 // source: grpc/gcp/transport_security_common.proto diff --git a/vendor/google.golang.org/grpc/credentials/tls.go b/vendor/google.golang.org/grpc/credentials/tls.go index 5dafd34edf9..4114358545e 100644 --- a/vendor/google.golang.org/grpc/credentials/tls.go +++ b/vendor/google.golang.org/grpc/credentials/tls.go @@ -27,9 +27,13 @@ import ( "net/url" "os" + "google.golang.org/grpc/grpclog" credinternal "google.golang.org/grpc/internal/credentials" + "google.golang.org/grpc/internal/envconfig" ) +var logger = grpclog.Component("credentials") + // TLSInfo contains the auth information for a TLS authenticated connection. // It implements the AuthInfo interface. type TLSInfo struct { @@ -112,6 +116,22 @@ func (c *tlsCreds) ClientHandshake(ctx context.Context, authority string, rawCon conn.Close() return nil, nil, ctx.Err() } + + // The negotiated protocol can be either of the following: + // 1. h2: When the server supports ALPN. Only HTTP/2 can be negotiated since + // it is the only protocol advertised by the client during the handshake. + // The tls library ensures that the server chooses a protocol advertised + // by the client. + // 2. "" (empty string): If the server doesn't support ALPN. ALPN is a requirement + // for using HTTP/2 over TLS. We can terminate the connection immediately. + np := conn.ConnectionState().NegotiatedProtocol + if np == "" { + if envconfig.EnforceALPNEnabled { + conn.Close() + return nil, nil, fmt.Errorf("credentials: cannot check peer: missing selected ALPN property") + } + logger.Warningf("Allowing TLS connection to server %q with ALPN disabled. TLS connections to servers with ALPN disabled will be disallowed in future grpc-go releases", cfg.ServerName) + } tlsInfo := TLSInfo{ State: conn.ConnectionState(), CommonAuthInfo: CommonAuthInfo{ @@ -131,8 +151,20 @@ func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) conn.Close() return nil, nil, err } + cs := conn.ConnectionState() + // The negotiated application protocol can be empty only if the client doesn't + // support ALPN. In such cases, we can close the connection since ALPN is required + // for using HTTP/2 over TLS. + if cs.NegotiatedProtocol == "" { + if envconfig.EnforceALPNEnabled { + conn.Close() + return nil, nil, fmt.Errorf("credentials: cannot check peer: missing selected ALPN property") + } else if logger.V(2) { + logger.Info("Allowing TLS connection from client with ALPN disabled. TLS connections with ALPN disabled will be disallowed in future grpc-go releases") + } + } tlsInfo := TLSInfo{ - State: conn.ConnectionState(), + State: cs, CommonAuthInfo: CommonAuthInfo{ SecurityLevel: PrivacyAndIntegrity, }, diff --git a/vendor/google.golang.org/grpc/dialoptions.go b/vendor/google.golang.org/grpc/dialoptions.go index 00273702b69..f5453d48a53 100644 --- a/vendor/google.golang.org/grpc/dialoptions.go +++ b/vendor/google.golang.org/grpc/dialoptions.go @@ -21,6 +21,7 @@ package grpc import ( "context" "net" + "net/url" "time" "google.golang.org/grpc/backoff" @@ -36,6 +37,11 @@ import ( "google.golang.org/grpc/stats" ) +const ( + // https://github.com/grpc/proposal/blob/master/A6-client-retries.md#limits-on-retries-and-hedges + defaultMaxCallAttempts = 5 +) + func init() { internal.AddGlobalDialOptions = func(opt ...DialOption) { globalDialOptions = append(globalDialOptions, opt...) @@ -43,6 +49,14 @@ func init() { internal.ClearGlobalDialOptions = func() { globalDialOptions = nil } + internal.AddGlobalPerTargetDialOptions = func(opt any) { + if ptdo, ok := opt.(perTargetDialOption); ok { + globalPerTargetDialOptions = append(globalPerTargetDialOptions, ptdo) + } + } + internal.ClearGlobalPerTargetDialOptions = func() { + globalPerTargetDialOptions = nil + } internal.WithBinaryLogger = withBinaryLogger internal.JoinDialOptions = newJoinDialOption internal.DisableGlobalDialOptions = newDisableGlobalDialOptions @@ -80,6 +94,7 @@ type dialOptions struct { idleTimeout time.Duration recvBufferPool SharedBufferPool defaultScheme string + maxCallAttempts int } // DialOption configures how we set up the connection. @@ -89,6 +104,19 @@ type DialOption interface { var globalDialOptions []DialOption +// perTargetDialOption takes a parsed target and returns a dial option to apply. +// +// This gets called after NewClient() parses the target, and allows per target +// configuration set through a returned DialOption. The DialOption will not take +// effect if specifies a resolver builder, as that Dial Option is factored in +// while parsing target. +type perTargetDialOption interface { + // DialOption returns a Dial Option to apply. + DialOptionForTarget(parsedTarget url.URL) DialOption +} + +var globalPerTargetDialOptions []perTargetDialOption + // EmptyDialOption does not alter the dial configuration. It can be embedded in // another structure to build custom dial options. // @@ -655,6 +683,7 @@ func defaultDialOptions() dialOptions { idleTimeout: 30 * time.Minute, recvBufferPool: nopBufferPool{}, defaultScheme: "dns", + maxCallAttempts: defaultMaxCallAttempts, } } @@ -712,6 +741,23 @@ func WithIdleTimeout(d time.Duration) DialOption { }) } +// WithMaxCallAttempts returns a DialOption that configures the maximum number +// of attempts per call (including retries and hedging) using the channel. +// Service owners may specify a higher value for these parameters, but higher +// values will be treated as equal to the maximum value by the client +// implementation. This mitigates security concerns related to the service +// config being transferred to the client via DNS. +// +// A value of 5 will be used if this dial option is not set or n < 2. +func WithMaxCallAttempts(n int) DialOption { + return newFuncDialOption(func(o *dialOptions) { + if n < 2 { + n = defaultMaxCallAttempts + } + o.maxCallAttempts = n + }) +} + // WithRecvBufferPool returns a DialOption that configures the ClientConn // to use the provided shared buffer pool for parsing incoming messages. Depending // on the application's workload, this could result in reduced memory allocation. diff --git a/vendor/google.golang.org/grpc/health/grpc_health_v1/health.pb.go b/vendor/google.golang.org/grpc/health/grpc_health_v1/health.pb.go index 6a93475a7fb..38b88350735 100644 --- a/vendor/google.golang.org/grpc/health/grpc_health_v1/health.pb.go +++ b/vendor/google.golang.org/grpc/health/grpc_health_v1/health.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.33.0 +// protoc-gen-go v1.34.1 // protoc v4.25.2 // source: grpc/health/v1/health.proto diff --git a/vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go b/vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go index 8f793e6e89f..51b736ba06e 100644 --- a/vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go +++ b/vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go @@ -17,7 +17,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 +// - protoc-gen-go-grpc v1.4.0 // - protoc v4.25.2 // source: grpc/health/v1/health.proto @@ -43,6 +43,10 @@ const ( // HealthClient is the client API for Health service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +// +// Health is gRPC's mechanism for checking whether a server is able to handle +// RPCs. Its semantics are documented in +// https://github.com/grpc/grpc/blob/master/doc/health-checking.md. type HealthClient interface { // Check gets the health of the specified service. If the requested service // is unknown, the call will fail with status NOT_FOUND. If the caller does @@ -126,6 +130,10 @@ func (x *healthWatchClient) Recv() (*HealthCheckResponse, error) { // HealthServer is the server API for Health service. // All implementations should embed UnimplementedHealthServer // for forward compatibility +// +// Health is gRPC's mechanism for checking whether a server is able to handle +// RPCs. Its semantics are documented in +// https://github.com/grpc/grpc/blob/master/doc/health-checking.md. type HealthServer interface { // Check gets the health of the specified service. If the requested service // is unknown, the call will fail with status NOT_FOUND. If the caller does diff --git a/vendor/google.golang.org/grpc/internal/backoff/backoff.go b/vendor/google.golang.org/grpc/internal/backoff/backoff.go index fed1c011a32..b15cf482d29 100644 --- a/vendor/google.golang.org/grpc/internal/backoff/backoff.go +++ b/vendor/google.golang.org/grpc/internal/backoff/backoff.go @@ -25,10 +25,10 @@ package backoff import ( "context" "errors" + "math/rand" "time" grpcbackoff "google.golang.org/grpc/backoff" - "google.golang.org/grpc/internal/grpcrand" ) // Strategy defines the methodology for backing off after a grpc connection @@ -67,7 +67,7 @@ func (bc Exponential) Backoff(retries int) time.Duration { } // Randomize backoff delays so that if a cluster of requests start at // the same time, they won't operate in lockstep. - backoff *= 1 + bc.Config.Jitter*(grpcrand.Float64()*2-1) + backoff *= 1 + bc.Config.Jitter*(rand.Float64()*2-1) if backoff < 0 { return 0 } diff --git a/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go b/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go index 9c915d9e4b2..d9064871394 100644 --- a/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go +++ b/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go @@ -40,6 +40,12 @@ var ( // ALTSMaxConcurrentHandshakes is the maximum number of concurrent ALTS // handshakes that can be performed. ALTSMaxConcurrentHandshakes = uint64FromEnv("GRPC_ALTS_MAX_CONCURRENT_HANDSHAKES", 100, 1, 100) + // EnforceALPNEnabled is set if TLS connections to servers with ALPN disabled + // should be rejected. The HTTP/2 protocol requires ALPN to be enabled, this + // option is present for backward compatibility. This option may be overridden + // by setting the environment variable "GRPC_ENFORCE_ALPN_ENABLED" to "true" + // or "false". + EnforceALPNEnabled = boolFromEnv("GRPC_ENFORCE_ALPN_ENABLED", false) ) func boolFromEnv(envVar string, def bool) bool { diff --git a/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go b/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go deleted file mode 100644 index 0126d6b5108..00000000000 --- a/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go +++ /dev/null @@ -1,100 +0,0 @@ -//go:build !go1.21 - -// TODO: when this file is deleted (after Go 1.20 support is dropped), delete -// all of grpcrand and call the rand package directly. - -/* - * - * Copyright 2018 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package grpcrand implements math/rand functions in a concurrent-safe way -// with a global random source, independent of math/rand's global source. -package grpcrand - -import ( - "math/rand" - "sync" - "time" -) - -var ( - r = rand.New(rand.NewSource(time.Now().UnixNano())) - mu sync.Mutex -) - -// Int implements rand.Int on the grpcrand global source. -func Int() int { - mu.Lock() - defer mu.Unlock() - return r.Int() -} - -// Int63n implements rand.Int63n on the grpcrand global source. -func Int63n(n int64) int64 { - mu.Lock() - defer mu.Unlock() - return r.Int63n(n) -} - -// Intn implements rand.Intn on the grpcrand global source. -func Intn(n int) int { - mu.Lock() - defer mu.Unlock() - return r.Intn(n) -} - -// Int31n implements rand.Int31n on the grpcrand global source. -func Int31n(n int32) int32 { - mu.Lock() - defer mu.Unlock() - return r.Int31n(n) -} - -// Float64 implements rand.Float64 on the grpcrand global source. -func Float64() float64 { - mu.Lock() - defer mu.Unlock() - return r.Float64() -} - -// Uint64 implements rand.Uint64 on the grpcrand global source. -func Uint64() uint64 { - mu.Lock() - defer mu.Unlock() - return r.Uint64() -} - -// Uint32 implements rand.Uint32 on the grpcrand global source. -func Uint32() uint32 { - mu.Lock() - defer mu.Unlock() - return r.Uint32() -} - -// ExpFloat64 implements rand.ExpFloat64 on the grpcrand global source. -func ExpFloat64() float64 { - mu.Lock() - defer mu.Unlock() - return r.ExpFloat64() -} - -// Shuffle implements rand.Shuffle on the grpcrand global source. -var Shuffle = func(n int, f func(int, int)) { - mu.Lock() - defer mu.Unlock() - r.Shuffle(n, f) -} diff --git a/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand_go1.21.go b/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand_go1.21.go deleted file mode 100644 index c37299af1ef..00000000000 --- a/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand_go1.21.go +++ /dev/null @@ -1,73 +0,0 @@ -//go:build go1.21 - -/* - * - * Copyright 2024 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package grpcrand implements math/rand functions in a concurrent-safe way -// with a global random source, independent of math/rand's global source. -package grpcrand - -import "math/rand" - -// This implementation will be used for Go version 1.21 or newer. -// For older versions, the original implementation with mutex will be used. - -// Int implements rand.Int on the grpcrand global source. -func Int() int { - return rand.Int() -} - -// Int63n implements rand.Int63n on the grpcrand global source. -func Int63n(n int64) int64 { - return rand.Int63n(n) -} - -// Intn implements rand.Intn on the grpcrand global source. -func Intn(n int) int { - return rand.Intn(n) -} - -// Int31n implements rand.Int31n on the grpcrand global source. -func Int31n(n int32) int32 { - return rand.Int31n(n) -} - -// Float64 implements rand.Float64 on the grpcrand global source. -func Float64() float64 { - return rand.Float64() -} - -// Uint64 implements rand.Uint64 on the grpcrand global source. -func Uint64() uint64 { - return rand.Uint64() -} - -// Uint32 implements rand.Uint32 on the grpcrand global source. -func Uint32() uint32 { - return rand.Uint32() -} - -// ExpFloat64 implements rand.ExpFloat64 on the grpcrand global source. -func ExpFloat64() float64 { - return rand.ExpFloat64() -} - -// Shuffle implements rand.Shuffle on the grpcrand global source. -var Shuffle = func(n int, f func(int, int)) { - rand.Shuffle(n, f) -} diff --git a/vendor/google.golang.org/grpc/internal/internal.go b/vendor/google.golang.org/grpc/internal/internal.go index 48d24bdb4e6..5d665398692 100644 --- a/vendor/google.golang.org/grpc/internal/internal.go +++ b/vendor/google.golang.org/grpc/internal/internal.go @@ -106,6 +106,14 @@ var ( // This is used in the 1.0 release of gcp/observability, and thus must not be // deleted or changed. ClearGlobalDialOptions func() + + // AddGlobalPerTargetDialOptions adds a PerTargetDialOption that will be + // configured for newly created ClientConns. + AddGlobalPerTargetDialOptions any // func (opt any) + // ClearGlobalPerTargetDialOptions clears the slice of global late apply + // dial options. + ClearGlobalPerTargetDialOptions func() + // JoinDialOptions combines the dial options passed as arguments into a // single dial option. JoinDialOptions any // func(...grpc.DialOption) grpc.DialOption @@ -126,7 +134,8 @@ var ( // deleted or changed. BinaryLogger any // func(binarylog.Logger) grpc.ServerOption - // SubscribeToConnectivityStateChanges adds a grpcsync.Subscriber to a provided grpc.ClientConn + // SubscribeToConnectivityStateChanges adds a grpcsync.Subscriber to a + // provided grpc.ClientConn. SubscribeToConnectivityStateChanges any // func(*grpc.ClientConn, grpcsync.Subscriber) // NewXDSResolverWithConfigForTesting creates a new xds resolver builder using @@ -184,25 +193,25 @@ var ( ChannelzTurnOffForTesting func() - // TriggerXDSResourceNameNotFoundForTesting triggers the resource-not-found - // error for a given resource type and name. This is usually triggered when - // the associated watch timer fires. For testing purposes, having this - // function makes events more predictable than relying on timer events. - TriggerXDSResourceNameNotFoundForTesting any // func(func(xdsresource.Type, string), string, string) error + // TriggerXDSResourceNotFoundForTesting causes the provided xDS Client to + // invoke resource-not-found error for the given resource type and name. + TriggerXDSResourceNotFoundForTesting any // func(xdsclient.XDSClient, xdsresource.Type, string) error - // TriggerXDSResourceNameNotFoundClient invokes the testing xDS Client - // singleton to invoke resource not found for a resource type name and - // resource name. - TriggerXDSResourceNameNotFoundClient any // func(string, string) error - - // FromOutgoingContextRaw returns the un-merged, intermediary contents of metadata.rawMD. + // FromOutgoingContextRaw returns the un-merged, intermediary contents of + // metadata.rawMD. FromOutgoingContextRaw any // func(context.Context) (metadata.MD, [][]string, bool) - // UserSetDefaultScheme is set to true if the user has overridden the default resolver scheme. + // UserSetDefaultScheme is set to true if the user has overridden the + // default resolver scheme. UserSetDefaultScheme bool = false + + // ShuffleAddressListForTesting pseudo-randomizes the order of addresses. n + // is the number of elements. swap swaps the elements with indexes i and j. + ShuffleAddressListForTesting any // func(n int, swap func(i, j int)) ) -// HealthChecker defines the signature of the client-side LB channel health checking function. +// HealthChecker defines the signature of the client-side LB channel health +// checking function. // // The implementation is expected to create a health checking RPC stream by // calling newStream(), watch for the health status of serviceName, and report diff --git a/vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go b/vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go index f3f52a59a86..4552db16b02 100644 --- a/vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go +++ b/vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go @@ -24,6 +24,7 @@ import ( "context" "encoding/json" "fmt" + "math/rand" "net" "os" "strconv" @@ -35,7 +36,6 @@ import ( "google.golang.org/grpc/grpclog" "google.golang.org/grpc/internal/backoff" "google.golang.org/grpc/internal/envconfig" - "google.golang.org/grpc/internal/grpcrand" "google.golang.org/grpc/internal/resolver/dns/internal" "google.golang.org/grpc/resolver" "google.golang.org/grpc/serviceconfig" @@ -63,6 +63,8 @@ var ( func init() { resolver.Register(NewBuilder()) internal.TimeAfterFunc = time.After + internal.TimeNowFunc = time.Now + internal.TimeUntilFunc = time.Until internal.NewNetResolver = newNetResolver internal.AddressDialer = addressDialer } @@ -209,12 +211,12 @@ func (d *dnsResolver) watcher() { err = d.cc.UpdateState(*state) } - var waitTime time.Duration + var nextResolutionTime time.Time if err == nil { // Success resolving, wait for the next ResolveNow. However, also wait 30 // seconds at the very least to prevent constantly re-resolving. backoffIndex = 1 - waitTime = MinResolutionInterval + nextResolutionTime = internal.TimeNowFunc().Add(MinResolutionInterval) select { case <-d.ctx.Done(): return @@ -223,13 +225,13 @@ func (d *dnsResolver) watcher() { } else { // Poll on an error found in DNS Resolver or an error received from // ClientConn. - waitTime = backoff.DefaultExponential.Backoff(backoffIndex) + nextResolutionTime = internal.TimeNowFunc().Add(backoff.DefaultExponential.Backoff(backoffIndex)) backoffIndex++ } select { case <-d.ctx.Done(): return - case <-internal.TimeAfterFunc(waitTime): + case <-internal.TimeAfterFunc(internal.TimeUntilFunc(nextResolutionTime)): } } } @@ -423,7 +425,7 @@ func chosenByPercentage(a *int) bool { if a == nil { return true } - return grpcrand.Intn(100)+1 <= *a + return rand.Intn(100)+1 <= *a } func canaryingSC(js string) string { diff --git a/vendor/google.golang.org/grpc/internal/resolver/dns/internal/internal.go b/vendor/google.golang.org/grpc/internal/resolver/dns/internal/internal.go index a7ecaf8d522..c0eae4f5f83 100644 --- a/vendor/google.golang.org/grpc/internal/resolver/dns/internal/internal.go +++ b/vendor/google.golang.org/grpc/internal/resolver/dns/internal/internal.go @@ -51,11 +51,22 @@ var ( // The following vars are overridden from tests. var ( // TimeAfterFunc is used by the DNS resolver to wait for the given duration - // to elapse. In non-test code, this is implemented by time.After. In test + // to elapse. In non-test code, this is implemented by time.After. In test // code, this can be used to control the amount of time the resolver is // blocked waiting for the duration to elapse. TimeAfterFunc func(time.Duration) <-chan time.Time + // TimeNowFunc is used by the DNS resolver to get the current time. + // In non-test code, this is implemented by time.Now. In test code, + // this can be used to control the current time for the resolver. + TimeNowFunc func() time.Time + + // TimeUntilFunc is used by the DNS resolver to calculate the remaining + // wait time for re-resolution. In non-test code, this is implemented by + // time.Until. In test code, this can be used to control the remaining + // time for resolver to wait for re-resolution. + TimeUntilFunc func(time.Time) time.Duration + // NewNetResolver returns the net.Resolver instance for the given target. NewNetResolver func(string) (NetResolver, error) diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_server.go b/vendor/google.golang.org/grpc/internal/transport/http2_server.go index cab0e2d3d44..b7091165b50 100644 --- a/vendor/google.golang.org/grpc/internal/transport/http2_server.go +++ b/vendor/google.golang.org/grpc/internal/transport/http2_server.go @@ -25,6 +25,7 @@ import ( "fmt" "io" "math" + "math/rand" "net" "net/http" "strconv" @@ -43,7 +44,6 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" "google.golang.org/grpc/internal/channelz" - "google.golang.org/grpc/internal/grpcrand" "google.golang.org/grpc/internal/grpcsync" "google.golang.org/grpc/keepalive" "google.golang.org/grpc/metadata" @@ -1440,7 +1440,7 @@ func getJitter(v time.Duration) time.Duration { } // Generate a jitter between +/- 10% of the value. r := int64(v / 10) - j := grpcrand.Int63n(2*r) - r + j := rand.Int63n(2*r) - r return time.Duration(j) } diff --git a/vendor/google.golang.org/grpc/picker_wrapper.go b/vendor/google.golang.org/grpc/picker_wrapper.go index 56e8aba783f..bdaa2130e48 100644 --- a/vendor/google.golang.org/grpc/picker_wrapper.go +++ b/vendor/google.golang.org/grpc/picker_wrapper.go @@ -22,7 +22,7 @@ import ( "context" "fmt" "io" - "sync" + "sync/atomic" "google.golang.org/grpc/balancer" "google.golang.org/grpc/codes" @@ -33,35 +33,43 @@ import ( "google.golang.org/grpc/status" ) +// pickerGeneration stores a picker and a channel used to signal that a picker +// newer than this one is available. +type pickerGeneration struct { + // picker is the picker produced by the LB policy. May be nil if a picker + // has never been produced. + picker balancer.Picker + // blockingCh is closed when the picker has been invalidated because there + // is a new one available. + blockingCh chan struct{} +} + // pickerWrapper is a wrapper of balancer.Picker. It blocks on certain pick // actions and unblock when there's a picker update. type pickerWrapper struct { - mu sync.Mutex - done bool - blockingCh chan struct{} - picker balancer.Picker + // If pickerGen holds a nil pointer, the pickerWrapper is closed. + pickerGen atomic.Pointer[pickerGeneration] statsHandlers []stats.Handler // to record blocking picker calls } func newPickerWrapper(statsHandlers []stats.Handler) *pickerWrapper { - return &pickerWrapper{ - blockingCh: make(chan struct{}), + pw := &pickerWrapper{ statsHandlers: statsHandlers, } + pw.pickerGen.Store(&pickerGeneration{ + blockingCh: make(chan struct{}), + }) + return pw } -// updatePicker is called by UpdateBalancerState. It unblocks all blocked pick. +// updatePicker is called by UpdateState calls from the LB policy. It +// unblocks all blocked pick. func (pw *pickerWrapper) updatePicker(p balancer.Picker) { - pw.mu.Lock() - if pw.done { - pw.mu.Unlock() - return - } - pw.picker = p - // pw.blockingCh should never be nil. - close(pw.blockingCh) - pw.blockingCh = make(chan struct{}) - pw.mu.Unlock() + old := pw.pickerGen.Swap(&pickerGeneration{ + picker: p, + blockingCh: make(chan struct{}), + }) + close(old.blockingCh) } // doneChannelzWrapper performs the following: @@ -98,20 +106,17 @@ func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer. var lastPickErr error for { - pw.mu.Lock() - if pw.done { - pw.mu.Unlock() + pg := pw.pickerGen.Load() + if pg == nil { return nil, balancer.PickResult{}, ErrClientConnClosing } - - if pw.picker == nil { - ch = pw.blockingCh + if pg.picker == nil { + ch = pg.blockingCh } - if ch == pw.blockingCh { + if ch == pg.blockingCh { // This could happen when either: // - pw.picker is nil (the previous if condition), or - // - has called pick on the current picker. - pw.mu.Unlock() + // - we have already called pick on the current picker. select { case <-ctx.Done(): var errStr string @@ -145,9 +150,8 @@ func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer. } } - ch = pw.blockingCh - p := pw.picker - pw.mu.Unlock() + ch = pg.blockingCh + p := pg.picker pickResult, err := p.Pick(info) if err != nil { @@ -197,24 +201,15 @@ func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer. } func (pw *pickerWrapper) close() { - pw.mu.Lock() - defer pw.mu.Unlock() - if pw.done { - return - } - pw.done = true - close(pw.blockingCh) + old := pw.pickerGen.Swap(nil) + close(old.blockingCh) } // reset clears the pickerWrapper and prepares it for being used again when idle // mode is exited. func (pw *pickerWrapper) reset() { - pw.mu.Lock() - defer pw.mu.Unlock() - if pw.done { - return - } - pw.blockingCh = make(chan struct{}) + old := pw.pickerGen.Swap(&pickerGeneration{blockingCh: make(chan struct{})}) + close(old.blockingCh) } // dropError is a wrapper error that indicates the LB policy wishes to drop the diff --git a/vendor/google.golang.org/grpc/resolver_wrapper.go b/vendor/google.golang.org/grpc/resolver_wrapper.go index 9dcc9780f89..c5fb45236fa 100644 --- a/vendor/google.golang.org/grpc/resolver_wrapper.go +++ b/vendor/google.golang.org/grpc/resolver_wrapper.go @@ -171,7 +171,7 @@ func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) { // ParseServiceConfig is called by resolver implementations to parse a JSON // representation of the service config. func (ccr *ccResolverWrapper) ParseServiceConfig(scJSON string) *serviceconfig.ParseResult { - return parseServiceConfig(scJSON) + return parseServiceConfig(scJSON, ccr.cc.dopts.maxCallAttempts) } // addChannelzTraceEvent adds a channelz trace event containing the new diff --git a/vendor/google.golang.org/grpc/service_config.go b/vendor/google.golang.org/grpc/service_config.go index 9da8fc8027d..2671c5ef69f 100644 --- a/vendor/google.golang.org/grpc/service_config.go +++ b/vendor/google.golang.org/grpc/service_config.go @@ -26,6 +26,7 @@ import ( "time" "google.golang.org/grpc/balancer" + "google.golang.org/grpc/balancer/pickfirst" "google.golang.org/grpc/codes" "google.golang.org/grpc/internal" "google.golang.org/grpc/internal/balancer/gracefulswitch" @@ -163,9 +164,11 @@ type jsonSC struct { } func init() { - internal.ParseServiceConfig = parseServiceConfig + internal.ParseServiceConfig = func(js string) *serviceconfig.ParseResult { + return parseServiceConfig(js, defaultMaxCallAttempts) + } } -func parseServiceConfig(js string) *serviceconfig.ParseResult { +func parseServiceConfig(js string, maxAttempts int) *serviceconfig.ParseResult { if len(js) == 0 { return &serviceconfig.ParseResult{Err: fmt.Errorf("no JSON service config provided")} } @@ -183,12 +186,12 @@ func parseServiceConfig(js string) *serviceconfig.ParseResult { } c := rsc.LoadBalancingConfig if c == nil { - name := PickFirstBalancerName + name := pickfirst.Name if rsc.LoadBalancingPolicy != nil { name = *rsc.LoadBalancingPolicy } if balancer.Get(name) == nil { - name = PickFirstBalancerName + name = pickfirst.Name } cfg := []map[string]any{{name: struct{}{}}} strCfg, err := json.Marshal(cfg) @@ -218,7 +221,7 @@ func parseServiceConfig(js string) *serviceconfig.ParseResult { WaitForReady: m.WaitForReady, Timeout: (*time.Duration)(m.Timeout), } - if mc.RetryPolicy, err = convertRetryPolicy(m.RetryPolicy); err != nil { + if mc.RetryPolicy, err = convertRetryPolicy(m.RetryPolicy, maxAttempts); err != nil { logger.Warningf("grpc: unmarshalling service config %s: %v", js, err) return &serviceconfig.ParseResult{Err: err} } @@ -264,7 +267,7 @@ func parseServiceConfig(js string) *serviceconfig.ParseResult { return &serviceconfig.ParseResult{Config: &sc} } -func convertRetryPolicy(jrp *jsonRetryPolicy) (p *internalserviceconfig.RetryPolicy, err error) { +func convertRetryPolicy(jrp *jsonRetryPolicy, maxAttempts int) (p *internalserviceconfig.RetryPolicy, err error) { if jrp == nil { return nil, nil } @@ -278,17 +281,16 @@ func convertRetryPolicy(jrp *jsonRetryPolicy) (p *internalserviceconfig.RetryPol return nil, nil } + if jrp.MaxAttempts < maxAttempts { + maxAttempts = jrp.MaxAttempts + } rp := &internalserviceconfig.RetryPolicy{ - MaxAttempts: jrp.MaxAttempts, + MaxAttempts: maxAttempts, InitialBackoff: time.Duration(jrp.InitialBackoff), MaxBackoff: time.Duration(jrp.MaxBackoff), BackoffMultiplier: jrp.BackoffMultiplier, RetryableStatusCodes: make(map[codes.Code]bool), } - if rp.MaxAttempts > 5 { - // TODO(retry): Make the max maxAttempts configurable. - rp.MaxAttempts = 5 - } for _, code := range jrp.RetryableStatusCodes { rp.RetryableStatusCodes[code] = true } diff --git a/vendor/google.golang.org/grpc/stream.go b/vendor/google.golang.org/grpc/stream.go index b54563e81cd..8051ef5b514 100644 --- a/vendor/google.golang.org/grpc/stream.go +++ b/vendor/google.golang.org/grpc/stream.go @@ -23,6 +23,7 @@ import ( "errors" "io" "math" + "math/rand" "strconv" "sync" "time" @@ -34,7 +35,6 @@ import ( "google.golang.org/grpc/internal/balancerload" "google.golang.org/grpc/internal/binarylog" "google.golang.org/grpc/internal/channelz" - "google.golang.org/grpc/internal/grpcrand" "google.golang.org/grpc/internal/grpcutil" imetadata "google.golang.org/grpc/internal/metadata" iresolver "google.golang.org/grpc/internal/resolver" @@ -699,7 +699,7 @@ func (a *csAttempt) shouldRetry(err error) (bool, error) { if max := float64(rp.MaxBackoff); cur > max { cur = max } - dur = time.Duration(grpcrand.Int63n(int64(cur))) + dur = time.Duration(rand.Int63n(int64(cur))) cs.numRetriesSincePushback++ } diff --git a/vendor/google.golang.org/grpc/version.go b/vendor/google.golang.org/grpc/version.go index a0b78289044..bafaef99be9 100644 --- a/vendor/google.golang.org/grpc/version.go +++ b/vendor/google.golang.org/grpc/version.go @@ -19,4 +19,4 @@ package grpc // Version is the current grpc version. -const Version = "1.64.1" +const Version = "1.65.0" diff --git a/vendor/modules.txt b/vendor/modules.txt index 1304d4292c0..aae9d650500 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -42,8 +42,8 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric # github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.44.0 ## explicit; go 1.20 github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping -# github.com/Microsoft/hcsshim v0.11.4 -## explicit; go 1.18 +# github.com/Microsoft/hcsshim v0.11.5 +## explicit; go 1.21 github.com/Microsoft/hcsshim/osversion # github.com/OneOfOne/xxhash v1.2.8 ## explicit; go 1.11 @@ -110,13 +110,13 @@ github.com/blang/semver/v4 # github.com/cenkalti/backoff/v4 v4.2.1 ## explicit; go 1.18 github.com/cenkalti/backoff/v4 -# github.com/cespare/xxhash/v2 v2.2.0 +# github.com/cespare/xxhash/v2 v2.3.0 ## explicit; go 1.11 github.com/cespare/xxhash/v2 # github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be ## explicit github.com/common-nighthawk/go-figure -# github.com/containerd/containerd v1.7.15 +# github.com/containerd/containerd v1.7.18 ## explicit; go 1.21 github.com/containerd/containerd/archive/compression github.com/containerd/containerd/content @@ -137,6 +137,9 @@ github.com/containerd/containerd/remotes/docker/schema1 github.com/containerd/containerd/remotes/errors github.com/containerd/containerd/tracing github.com/containerd/containerd/version +# github.com/containerd/errdefs v0.1.0 +## explicit; go 1.20 +github.com/containerd/errdefs # github.com/containerd/log v0.1.0 ## explicit; go 1.20 github.com/containerd/log @@ -423,7 +426,7 @@ github.com/onsi/gomega/types # github.com/open-policy-agent/cert-controller v0.10.1 ## explicit; go 1.20 github.com/open-policy-agent/cert-controller/pkg/rotator -# github.com/open-policy-agent/frameworks/constraint v0.0.0-20240703202613-6687ba5fafc8 +# github.com/open-policy-agent/frameworks/constraint v0.0.0-20240703202613-6687ba5fafc8 => /mount/d/go/src/github.com/open-policy-agent/frameworks/constraint ## explicit; go 1.21 github.com/open-policy-agent/frameworks/constraint/deploy github.com/open-policy-agent/frameworks/constraint/pkg/apis @@ -443,6 +446,7 @@ github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/k8scel/tra github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/rego github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/rego/schema github.com/open-policy-agent/frameworks/constraint/pkg/client/errors +github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews github.com/open-policy-agent/frameworks/constraint/pkg/core/constraints github.com/open-policy-agent/frameworks/constraint/pkg/core/templates github.com/open-policy-agent/frameworks/constraint/pkg/externaldata @@ -451,7 +455,7 @@ github.com/open-policy-agent/frameworks/constraint/pkg/instrumentation github.com/open-policy-agent/frameworks/constraint/pkg/regorewriter github.com/open-policy-agent/frameworks/constraint/pkg/schema github.com/open-policy-agent/frameworks/constraint/pkg/types -# github.com/open-policy-agent/opa v0.64.1 +# github.com/open-policy-agent/opa v0.66.0 ## explicit; go 1.21 github.com/open-policy-agent/opa/ast github.com/open-policy-agent/opa/ast/internal/scanner @@ -543,7 +547,7 @@ github.com/open-policy-agent/opa/version # github.com/opencontainers/go-digest v1.0.0 ## explicit; go 1.13 github.com/opencontainers/go-digest -# github.com/opencontainers/image-spec v1.1.0-rc6 +# github.com/opencontainers/image-spec v1.1.0 ## explicit; go 1.18 github.com/opencontainers/image-spec/specs-go github.com/opencontainers/image-spec/specs-go/v1 @@ -856,8 +860,8 @@ google.golang.org/genproto/googleapis/api/monitoredres google.golang.org/genproto/googleapis/rpc/code google.golang.org/genproto/googleapis/rpc/errdetails google.golang.org/genproto/googleapis/rpc/status -# google.golang.org/grpc v1.64.1 -## explicit; go 1.19 +# google.golang.org/grpc v1.65.0 +## explicit; go 1.21 google.golang.org/grpc google.golang.org/grpc/attributes google.golang.org/grpc/backoff @@ -866,6 +870,7 @@ google.golang.org/grpc/balancer/base google.golang.org/grpc/balancer/grpclb google.golang.org/grpc/balancer/grpclb/grpc_lb_v1 google.golang.org/grpc/balancer/grpclb/state +google.golang.org/grpc/balancer/pickfirst google.golang.org/grpc/balancer/roundrobin google.golang.org/grpc/binarylog/grpc_binarylog_v1 google.golang.org/grpc/channelz @@ -898,7 +903,6 @@ google.golang.org/grpc/internal/credentials google.golang.org/grpc/internal/envconfig google.golang.org/grpc/internal/googlecloud google.golang.org/grpc/internal/grpclog -google.golang.org/grpc/internal/grpcrand google.golang.org/grpc/internal/grpcsync google.golang.org/grpc/internal/grpcutil google.golang.org/grpc/internal/idle @@ -1548,3 +1552,4 @@ sigs.k8s.io/structured-merge-diff/v4/value ## explicit; go 1.12 sigs.k8s.io/yaml sigs.k8s.io/yaml/goyaml.v2 +# github.com/open-policy-agent/frameworks/constraint => /mount/d/go/src/github.com/open-policy-agent/frameworks/constraint From 45b3a0bf93628a86b1ca3199a6a15fdf4cf3cc1f Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Thu, 25 Jul 2024 16:51:57 +0000 Subject: [PATCH 02/23] fixing tests Signed-off-by: Jaydip Gabani --- pkg/controller/constraint/constraint_controller.go | 9 +++++---- pkg/gator/fixtures/fixtures.go | 3 --- pkg/gator/test/test_test.go | 6 +++--- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/pkg/controller/constraint/constraint_controller.go b/pkg/controller/constraint/constraint_controller.go index bb5627852ad..b81679ae765 100755 --- a/pkg/controller/constraint/constraint_controller.go +++ b/pkg/controller/constraint/constraint_controller.go @@ -309,18 +309,19 @@ func (r *ReconcileConstraint) Reconcile(ctx context.Context, request reconcile.R } return reconcile.Result{}, err } - isVAPBGenerationEnabled := generateVAPB && IsVapAPIEnabled() && HasVAPCel(ct) - if generateVAPB != isVAPBGenerationEnabled { + if generateVAPB { if !IsVapAPIEnabled() { r.log.V(1).Info("Warning: VAP API is not enabled, cannot create VAPBinding") + generateVAPB = false } if !HasVAPCel(ct) { r.log.V(1).Info("Warning: ConstraintTemplate does not contain VAP-style CEL source, cannot create VAPBinding") + generateVAPB = false } } - r.log.Info("constraint controller", "generateVAPB", isVAPBGenerationEnabled) + r.log.Info("constraint controller", "generateVAPB", generateVAPB) // generate vapbinding resources - if isVAPBGenerationEnabled { + if generateVAPB { currentVapBinding := &admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding{} vapBindingName := fmt.Sprintf("gatekeeper-%s", instance.GetName()) log.Info("check if vapbinding exists", "vapBindingName", vapBindingName) diff --git a/pkg/gator/fixtures/fixtures.go b/pkg/gator/fixtures/fixtures.go index 1b4e6e944e2..8089ce3cb34 100644 --- a/pkg/gator/fixtures/fixtures.go +++ b/pkg/gator/fixtures/fixtures.go @@ -225,9 +225,6 @@ spec: - enforcementPoints: - name: gator.gatekeeper.sh action: deny - - enforcementPoints: - - name: gator.gatekeeper.sh - action: warn ` ConstraintAuditValidate = ` diff --git a/pkg/gator/test/test_test.go b/pkg/gator/test/test_test.go index a5724cafc14..83163587dcc 100644 --- a/pkg/gator/test/test_test.go +++ b/pkg/gator/test/test_test.go @@ -200,7 +200,7 @@ func TestTest(t *testing.T) { Msg: "never validate", Constraint: constraintGatorValidate, EnforcementAction: "scoped", - ScopedEnforcementActions: []string{"deny", "warn"}, + ScopedEnforcementActions: []string{"deny"}, }, }, { @@ -209,7 +209,7 @@ func TestTest(t *testing.T) { Msg: "never validate", Constraint: constraintGatorValidate, EnforcementAction: "scoped", - ScopedEnforcementActions: []string{"deny", "warn"}, + ScopedEnforcementActions: []string{"deny"}, }, }, { @@ -218,7 +218,7 @@ func TestTest(t *testing.T) { Msg: "never validate", Constraint: constraintGatorValidate, EnforcementAction: "scoped", - ScopedEnforcementActions: []string{"deny", "warn"}, + ScopedEnforcementActions: []string{"deny"}, }, }, }, From 92426e62e281bb26b60e404447f596584a4c1f21 Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Thu, 25 Jul 2024 17:26:17 +0000 Subject: [PATCH 03/23] fixing bats Signed-off-by: Jaydip Gabani --- test/bats/test.bats | 44 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/test/bats/test.bats b/test/bats/test.bats index 6fd6186cb8e..3bdeec41d90 100644 --- a/test/bats/test.bats +++ b/test/bats/test.bats @@ -292,12 +292,26 @@ __required_labels_audit_test() { return 3 fi - local violations=$(echo "${cstr}" | jq -r '.status.violations[].enforcementAction') + local enforcementActions=$(echo "${cstr}" | jq -r '.status.violations[].enforcementAction') local match=true - for violation in $violations; do - if [[ "${violation}" != "deny" ]]; then - echo "Mismatch found: Enforcement action is ${violation}, expected deny" + for enforcementAction in $enforcementActions; do + if [[ "${enforcementAction}" != "scoped" ]]; then + echo "Mismatch found: Enforcement action is ${enforcementAction}, expected scoped" + match=false + fi + done + + if [[ "${match}" == "false" ]]; then + return 3 + fi + + local scopedEnforcementActions=$(echo "${cstr}" | jq -r '.status.violations[].enforcementActions[]') + local match=true + + for scopedEnforcementAction in $scopedEnforcementActions; do + if [[ "${scopedEnforcementAction}" != "deny" ]]; then + echo "Mismatch found: Enforcement action is ${scopedEnforcementAction}, expected deny" match=false fi done @@ -326,12 +340,26 @@ __required_labels_audit_test() { return 3 fi - local violations=$(echo "${cstr}" | jq -r '.status.violations[].enforcementAction') + local enforcementActions=$(echo "${cstr}" | jq -r '.status.violations[].enforcementAction') + local match=true + + for enforcementAction in $enforcementActions; do + if [[ "${enforcementAction}" != "scoped" ]]; then + echo "Mismatch found: Enforcement action is ${enforcementAction}, expected scoped" + match=false + fi + done + + if [[ "${match}" == "false" ]]; then + return 3 + fi + + local scopedEnforcementActions=$(echo "${cstr}" | jq -r '.status.violations[].enforcementActions') local match=true - for violation in $violations; do - if [[ "${violation}" != "warn" ]]; then - echo "Mismatch found: Enforcement action is ${violation}, expected warn" + for scopedEnforcementAction in $scopedEnforcementActions; do + if [[ "${scopedEnforcementAction}" != "warn" ]]; then + echo "Mismatch found: Enforcement action is ${scopedEnforcementAction}, expected warn" match=false fi done From 641315ba26b2107feac9a6a2454ce781876a1a40 Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Thu, 25 Jul 2024 17:37:33 +0000 Subject: [PATCH 04/23] fixing bats test Signed-off-by: Jaydip Gabani --- test/bats/test.bats | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/bats/test.bats b/test/bats/test.bats index 3bdeec41d90..e000324703c 100644 --- a/test/bats/test.bats +++ b/test/bats/test.bats @@ -354,7 +354,7 @@ __required_labels_audit_test() { return 3 fi - local scopedEnforcementActions=$(echo "${cstr}" | jq -r '.status.violations[].enforcementActions') + local scopedEnforcementActions=$(echo "${cstr}" | jq -r '.status.violations[].enforcementActions[]') local match=true for scopedEnforcementAction in $scopedEnforcementActions; do From 2055dcbeaed7833a29c533ec580c7dbfc0988183 Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Fri, 26 Jul 2024 02:11:07 +0000 Subject: [PATCH 05/23] fixing duplicate review option in webhook Signed-off-by: Jaydip Gabani --- pkg/webhook/policy.go | 2 +- .../constraint/pkg/apis/constraints/apis.go | 27 ++++++++++--------- .../constraint/pkg/client/client.go | 20 ++------------ .../constraint/pkg/client/client_opts.go | 1 + .../pkg/client/constraint_client.go | 17 ++++++------ .../constraint/pkg/client/template_client.go | 4 +-- 6 files changed, 29 insertions(+), 42 deletions(-) diff --git a/pkg/webhook/policy.go b/pkg/webhook/policy.go index ec228a47d33..63b28338d7d 100644 --- a/pkg/webhook/policy.go +++ b/pkg/webhook/policy.go @@ -605,7 +605,7 @@ func (h *validationHandler) reviewRequest(ctx context.Context, req *admission.Re } func (h *validationHandler) review(ctx context.Context, review interface{}, trace bool, dump bool) (*rtypes.Responses, error) { - resp, err := h.opa.Review(ctx, review, reviews.SourceEP(util.WebhookEnforcementPoint), reviews.Tracing(trace), reviews.Stats(*logStatsAdmission), reviews.SourceEP(util.WebhookEnforcementPoint)) + resp, err := h.opa.Review(ctx, review, reviews.SourceEP(util.WebhookEnforcementPoint), reviews.Tracing(trace), reviews.Stats(*logStatsAdmission)) if resp != nil && trace { h.log.Info(resp.TraceDump()) } diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go index a64e7a3d299..f4bee561684 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go @@ -146,19 +146,20 @@ func getNestedFieldAsArray(obj map[string]interface{}, fields ...string) ([]inte // Helper function to convert a value to a []ScopedEnforcementAction. func convertToSliceScopedEnforcementAction(value interface{}) ([]ScopedEnforcementAction, error) { var result []ScopedEnforcementAction - if arr, ok := value.([]interface{}); ok { - for _, v := range arr { - if m, ok := v.(map[string]interface{}); ok { - scopedEA := &ScopedEnforcementAction{} - if err := runtime.DefaultUnstructuredConverter.FromUnstructured(m, scopedEA); err != nil { - return nil, err - } - result = append(result, *scopedEA) - } else { - return nil, fmt.Errorf("scopedEnforcementActions value must be a []scopedEnforcementAction{action: string, enforcementPoints: []EnforcementPoint{name: string}}") - } + arr, ok := value.([]interface{}) + if !ok { + return nil, fmt.Errorf("scopedEnforcementActions value must be a []scopedEnforcementAction{action: string, enforcementPoints: []EnforcementPoint{name: string}}") + } + for _, v := range arr { + m, ok := v.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("scopedEnforcementActions value must be a []scopedEnforcementAction{action: string, enforcementPoints: []EnforcementPoint{name: string}}") + } + scopedEA := &ScopedEnforcementAction{} + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(m, scopedEA); err != nil { + return nil, err } - return result, nil + result = append(result, *scopedEA) } - return nil, fmt.Errorf("scopedEnforcementActions value must be a []scopedEnforcementAction{action: string, enforcementPoints: []EnforcementPoint{name: string}}") + return result, nil } diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client.go index 563734d1442..959448968cc 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client.go @@ -638,24 +638,13 @@ func (c *Client) Review(ctx context.Context, obj interface{}, opts ...reviews.Re // Check if a source enforcement point is specified in the configuration if cfg.SourceEP != "" { - // Initialize enforceAll as false. It will be used to determine if all enforcement points should be enforced - enforceAll := false // Iterate through the client's enforcement points for _, ep := range c.enforcementPoints { - // Check if the current enforcement point indicates all enforcement points should be enforced - if ep == apiconstraints.AllEnforcementPoints { - enforceAll = true - } - // If the specified source enforcement point matches the current enforcement point, add it to the list - if cfg.SourceEP == ep { + if ep == apiconstraints.AllEnforcementPoints || cfg.SourceEP == ep { eps = append(eps, ep) - break // Exit the loop since the matching enforcement point is found + break } } - // If enforceAll is true, add the source enforcement point to the list of enforcement points - if enforceAll { - eps = append(eps, cfg.SourceEP) - } // If no enforcement points match the source enforcement point, return nil indicating no review should be run if eps == nil { return nil, nil @@ -667,11 +656,6 @@ func (c *Client) Review(ctx context.Context, obj interface{}, opts ...reviews.Re eps = c.enforcementPoints } - // If there are no enforcement points specified, default to using all enforcement points - if eps == nil { - eps = []string{apiconstraints.AllEnforcementPoints} - } - responses := types.NewResponses() errMap := make(clienterrors.ErrorMap) diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client_opts.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client_opts.go index 775b2d9aedd..13da142809a 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client_opts.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client_opts.go @@ -63,6 +63,7 @@ func Driver(d drivers.Driver) Opt { } client.drivers[d.Name()] = d client.driverPriority[d.Name()] = len(client.drivers) + client.enforcementPoints = []string{"*"} return nil } } diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/constraint_client.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/constraint_client.go index 0d21481a012..fcf32c7253e 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/constraint_client.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/constraint_client.go @@ -32,34 +32,35 @@ func (c *constraintClient) getConstraint() *unstructured.Unstructured { return c.constraint.DeepCopy() } -func (c *constraintClient) matches(target string, review interface{}, sourceEPs ...string) *constraintMatchResult { +func (c *constraintClient) matches(target string, review interface{}, enforcementPoints ...string) *constraintMatchResult { matcher, found := c.matchers[target] if !found { return nil } + // Initialize a map to track unique enforcement actions enforcementActions := make(map[string]bool) if apiconstraints.IsEnforcementActionScoped(c.enforcementAction) { - // Initialize a map to track unique enforcement actions // Iterate over the provided source enforcement points (EPs) - for _, ep := range sourceEPs { + for _, ep := range enforcementPoints { var actions []string - // Check if there are predefined actions for the current EP - if acts, found := c.enforcementActionsForEP[ep]; found { - actions = acts // Use the predefined actions if found - } else if ep == "*" { + if ep == "*" { // If the EP is "*", aggregate actions from all EPs for _, acts := range c.enforcementActionsForEP { actions = append(actions, acts...) } } + // Check if there are predefined actions for the current EP + if acts, found := c.enforcementActionsForEP[ep]; found { + actions = append(actions, acts...) // Use the predefined actions if found + } // Mark each action as true in the map to ensure uniqueness for _, act := range actions { enforcementActions[act] = true } } - } + // If enforcement action is scoped, constraint does not include enforcement point that needs to be enforced then there is no action to be taken. // If no enforcement actions are found, return nil if len(enforcementActions) == 0 && apiconstraints.IsEnforcementActionScoped(c.enforcementAction) { return nil diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/template_client.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/template_client.go index 9017ebb35c9..8e1d4ceeb3a 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/template_client.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/template_client.go @@ -181,11 +181,11 @@ func (e *templateClient) RemoveConstraint(name string) { // against the passed review. // // ignoredTargets specifies the targets whose matchers to not run. -func (e *templateClient) Matches(target string, review interface{}, sourceEPs []string) map[string]constraintMatchResult { +func (e *templateClient) Matches(target string, review interface{}, enforcementPoints []string) map[string]constraintMatchResult { result := make(map[string]constraintMatchResult) for name, constraint := range e.constraints { - cResult := constraint.matches(target, review, sourceEPs...) + cResult := constraint.matches(target, review, enforcementPoints...) if cResult != nil { result[name] = *cResult } From c5c6fbec88ac36f1a73d96ad07fa21b409903f25 Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Fri, 26 Jul 2024 06:28:21 +0000 Subject: [PATCH 06/23] renaming sourceEP -> EnforcementPoint Signed-off-by: Jaydip Gabani --- pkg/audit/manager.go | 6 +- .../constrainttemplate_controller_test.go | 6 +- pkg/gator/test/test.go | 4 +- pkg/gator/verify/runner.go | 4 +- pkg/target/target_integration_test.go | 6 +- pkg/webhook/policy.go | 2 +- .../constraint/pkg/apis/constraints/apis.go | 56 +++++++++---------- .../constraint/pkg/client/client.go | 26 +++------ .../pkg/client/constraint_client.go | 2 +- .../constraint/pkg/client/errors.go | 19 ++++--- .../pkg/client/reviews/review_opts.go | 10 ++-- .../constraint/pkg/client/template_client.go | 34 ++--------- 12 files changed, 68 insertions(+), 107 deletions(-) diff --git a/pkg/audit/manager.go b/pkg/audit/manager.go index 0128cda82cf..78b7dcc3a1a 100644 --- a/pkg/audit/manager.go +++ b/pkg/audit/manager.go @@ -609,7 +609,7 @@ func (am *Manager) auditFromCache(ctx context.Context) ([]Result, []error) { Object: obj, Namespace: ns, } - resp, err := am.opa.Review(ctx, au, reviews.SourceEP(util.AuditEnforcementPoint), reviews.Stats(*logStatsAudit), reviews.SourceEP(util.AuditEnforcementPoint)) + resp, err := am.opa.Review(ctx, au, reviews.EnforcementPoint(util.AuditEnforcementPoint), reviews.Stats(*logStatsAudit)) if err != nil { am.log.Error(err, fmt.Sprintf("Unable to review object from audit cache %v %s/%s", obj.GroupVersionKind().String(), obj.GetNamespace(), obj.GetName())) continue @@ -699,7 +699,7 @@ func (am *Manager) reviewObjects(ctx context.Context, kind string, folderCount i Source: mutationtypes.SourceTypeOriginal, } - resp, err := am.opa.Review(ctx, augmentedObj, reviews.SourceEP(util.AuditEnforcementPoint), reviews.Stats(*logStatsAudit), reviews.SourceEP(util.AuditEnforcementPoint)) + resp, err := am.opa.Review(ctx, augmentedObj, reviews.EnforcementPoint(util.AuditEnforcementPoint), reviews.Stats(*logStatsAudit)) if err != nil { am.log.Error(err, "Unable to review object from file", "fileName", fileName, "objNs", objNs) continue @@ -723,7 +723,7 @@ func (am *Manager) reviewObjects(ctx context.Context, kind string, folderCount i Namespace: ns, Source: mutationtypes.SourceTypeGenerated, } - resultantResp, err := am.opa.Review(ctx, au, reviews.SourceEP(util.AuditEnforcementPoint), reviews.Stats(*logStatsAudit), reviews.SourceEP(util.AuditEnforcementPoint)) + resultantResp, err := am.opa.Review(ctx, au, reviews.EnforcementPoint(util.AuditEnforcementPoint), reviews.Stats(*logStatsAudit)) if err != nil { am.log.Error(err, "Unable to review expanded object", "objName", (*resultant.Obj).GetName(), "objNs", ns) continue diff --git a/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go b/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go index 1edbe2deb40..0a950809eed 100644 --- a/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go +++ b/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go @@ -657,7 +657,7 @@ func TestReconcile(t *testing.T) { Name: "FooNamespace", Object: runtime.RawExtension{Object: ns}, } - resp, err := cfClient.Review(ctx, req, reviews.SourceEP(util.AuditEnforcementPoint)) + resp, err := cfClient.Review(ctx, req, reviews.EnforcementPoint(util.AuditEnforcementPoint)) if err != nil { t.Fatal(err) } @@ -851,7 +851,7 @@ func TestReconcile(t *testing.T) { Name: "FooNamespace", Object: runtime.RawExtension{Object: ns}, } - resp, err := cfClient.Review(ctx, req, reviews.SourceEP(util.AuditEnforcementPoint)) + resp, err := cfClient.Review(ctx, req, reviews.EnforcementPoint(util.AuditEnforcementPoint)) if err != nil { t.Fatal(err) } @@ -872,7 +872,7 @@ func TestReconcile(t *testing.T) { err = retry.OnError(testutils.ConstantRetry, func(_ error) bool { return true }, func() error { - resp, err := cfClient.Review(ctx, req, reviews.SourceEP(util.AuditEnforcementPoint)) + resp, err := cfClient.Review(ctx, req, reviews.EnforcementPoint(util.AuditEnforcementPoint)) if err != nil { return err } diff --git a/pkg/gator/test/test.go b/pkg/gator/test/test.go index f5c97f56475..c7710a60351 100644 --- a/pkg/gator/test/test.go +++ b/pkg/gator/test/test.go @@ -115,7 +115,7 @@ func Test(objs []*unstructured.Unstructured, tOpts Opts) (*GatorResponses, error Source: mutationtypes.SourceTypeOriginal, } - review, err := client.Review(ctx, au, reviews.SourceEP(util.GatorEnforcementPoint)) + review, err := client.Review(ctx, au, reviews.EnforcementPoint(util.GatorEnforcementPoint)) if err != nil { return nil, fmt.Errorf("reviewing %v %s/%s: %w", obj.GroupVersionKind(), obj.GetNamespace(), obj.GetName(), err) @@ -132,7 +132,7 @@ func Test(objs []*unstructured.Unstructured, tOpts Opts) (*GatorResponses, error Namespace: ns, Source: mutationtypes.SourceTypeGenerated, } - resultantReview, err := client.Review(ctx, au, reviews.SourceEP(util.GatorEnforcementPoint)) + resultantReview, err := client.Review(ctx, au, reviews.EnforcementPoint(util.GatorEnforcementPoint)) if err != nil { return nil, fmt.Errorf("reviewing expanded resource %v %s/%s: %w", resultant.Obj.GroupVersionKind(), resultant.Obj.GetNamespace(), resultant.Obj.GetName(), err) diff --git a/pkg/gator/verify/runner.go b/pkg/gator/verify/runner.go index 5fca9af7d20..4f3126e5d0c 100644 --- a/pkg/gator/verify/runner.go +++ b/pkg/gator/verify/runner.go @@ -327,7 +327,7 @@ func (r *Runner) runReview(ctx context.Context, newClient func() (gator.Client, Object: *toReview, Source: mutationtypes.SourceTypeOriginal, } - return c.Review(ctx, au, reviews.SourceEP(util.GatorEnforcementPoint)) + return c.Review(ctx, au, reviews.EnforcementPoint(util.GatorEnforcementPoint)) } func (r *Runner) validateAndReviewAdmissionReviewRequest(ctx context.Context, c gator.Client, toReview *unstructured.Unstructured) (*types.Responses, error) { @@ -370,7 +370,7 @@ func (r *Runner) validateAndReviewAdmissionReviewRequest(ctx context.Context, c Source: mutationtypes.SourceTypeOriginal, } - return c.Review(ctx, arr, reviews.SourceEP(util.GatorEnforcementPoint)) + return c.Review(ctx, arr, reviews.EnforcementPoint(util.GatorEnforcementPoint)) } func (r *Runner) addInventory(ctx context.Context, c gator.Client, suiteDir, inventoryPath string) error { diff --git a/pkg/target/target_integration_test.go b/pkg/target/target_integration_test.go index d713bc19460..d952d04ca11 100644 --- a/pkg/target/target_integration_test.go +++ b/pkg/target/target_integration_test.go @@ -469,7 +469,7 @@ func TestConstraintEnforcement(t *testing.T) { } fullReq := &AugmentedReview{Namespace: tc.ns, AdmissionRequest: req} - res, err := c.Review(ctx, fullReq, reviews.SourceEP(util.AuditEnforcementPoint), reviews.Tracing(true)) + res, err := c.Review(ctx, fullReq, reviews.EnforcementPoint(util.AuditEnforcementPoint), reviews.Tracing(true)) if err != nil { t.Errorf("Error reviewing request: %s", err) } @@ -498,7 +498,7 @@ func TestConstraintEnforcement(t *testing.T) { } fullReq2 := &AugmentedReview{Namespace: tc.ns, AdmissionRequest: req2} - res2, err := c.Review(ctx, fullReq2, reviews.SourceEP(util.AuditEnforcementPoint), reviews.Tracing(true)) + res2, err := c.Review(ctx, fullReq2, reviews.EnforcementPoint(util.AuditEnforcementPoint), reviews.Tracing(true)) if err != nil { t.Errorf("Error reviewing OldObject request: %s", err) } @@ -511,7 +511,7 @@ func TestConstraintEnforcement(t *testing.T) { } fullReq3 := &AugmentedUnstructured{Namespace: tc.ns, Object: *tc.obj} - res3, err := c.Review(ctx, fullReq3, reviews.SourceEP(util.AuditEnforcementPoint), reviews.Tracing(true)) + res3, err := c.Review(ctx, fullReq3, reviews.EnforcementPoint(util.AuditEnforcementPoint), reviews.Tracing(true)) if err != nil { t.Errorf("Error reviewing AugmentedUnstructured request: %s", err) } diff --git a/pkg/webhook/policy.go b/pkg/webhook/policy.go index 63b28338d7d..4202a74231e 100644 --- a/pkg/webhook/policy.go +++ b/pkg/webhook/policy.go @@ -605,7 +605,7 @@ func (h *validationHandler) reviewRequest(ctx context.Context, req *admission.Re } func (h *validationHandler) review(ctx context.Context, review interface{}, trace bool, dump bool) (*rtypes.Responses, error) { - resp, err := h.opa.Review(ctx, review, reviews.SourceEP(util.WebhookEnforcementPoint), reviews.Tracing(trace), reviews.Stats(*logStatsAdmission)) + resp, err := h.opa.Review(ctx, review, reviews.EnforcementPoint(util.WebhookEnforcementPoint), reviews.Tracing(trace), reviews.Stats(*logStatsAdmission)) if resp != nil && trace { h.log.Info(resp.TraceDump()) } diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go index f4bee561684..e711860b3b0 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go @@ -70,17 +70,17 @@ func IsEnforcementActionScoped(action string) bool { } // GetEnforcementActionsForEP returns a map of enforcement actions for enforcement points passed in. -func GetEnforcementActionsForEP(constraint *unstructured.Unstructured, eps []string) (map[string]map[string]bool, error) { +func GetEnforcementActionsForEP(constraint *unstructured.Unstructured, eps []string) (map[string][]string, error) { if len(eps) == 0 { return nil, fmt.Errorf("enforcement points must be provided to get enforcement actions") } scopedActions, found, err := getNestedFieldAsArray(constraint.Object, "spec", "scopedEnforcementActions") if err != nil { - return nil, fmt.Errorf("%w: invalid spec.enforcementActionPerEP", ErrInvalidConstraint) + return nil, fmt.Errorf("%w: invalid spec.scopedEnforcementActions", ErrInvalidConstraint) } if !found { - return nil, fmt.Errorf("%w: spec.scopedEnforcementAction must be defined", ErrMissingRequiredField) + return nil, fmt.Errorf("%w: spec.scopedEnforcementActions must be defined", ErrMissingRequiredField) } scopedEnforcementActions, err := convertToSliceScopedEnforcementAction(scopedActions) @@ -88,44 +88,38 @@ func GetEnforcementActionsForEP(constraint *unstructured.Unstructured, eps []str return nil, fmt.Errorf("%w: %w", ErrInvalidConstraint, err) } - // Flag to indicate if all enforcement points should be enforced - enforceAll := false - // Initialize a map to hold enforcement actions for each enforcement point - actionsForEPs := make(map[string]map[string]bool) - // Populate the actionsForEPs map with enforcement points from eps, initializing their action maps - for _, enforcementPoint := range eps { - if enforcementPoint == AllEnforcementPoints { - enforceAll = true // Set enforceAll to true if the special identifier for all enforcement points is found - } - actionsForEPs[enforcementPoint] = make(map[string]bool) // Initialize the action map for the enforcement point + enforcementPointsToActionsMap := make(map[string]map[string]bool) + for _, ep := range eps { + enforcementPointsToActionsMap[ep] = make(map[string]bool) } - - // Iterate over the scoped enforcement actions to populate actions for each enforcement point for _, scopedEA := range scopedEnforcementActions { for _, enforcementPoint := range scopedEA.EnforcementPoints { epName := strings.ToLower(enforcementPoint.Name) ea := strings.ToLower(scopedEA.Action) - // If enforceAll is true, or the enforcement point is explicitly listed, initialize its action map - if _, ok := actionsForEPs[epName]; !ok && enforceAll { - actionsForEPs[epName] = make(map[string]bool) - } - // Skip adding actions for enforcement points not in the list unless enforceAll is true - if _, ok := actionsForEPs[epName]; !ok && epName != AllEnforcementPoints { - continue - } - // If the enforcement point is the special identifier for all, apply the action to all enforcement points - switch epName { - case AllEnforcementPoints: - for ep := range actionsForEPs { - actionsForEPs[ep][ea] = true + if epName == AllEnforcementPoints { + for _, ep := range eps { + enforcementPointsToActionsMap[ep][ea] = true } - default: - actionsForEPs[epName][ea] = true + break + } + if _, ok := enforcementPointsToActionsMap[epName]; ok { + enforcementPointsToActionsMap[epName][ea] = true } } } + enforcementActionsForEPs := make(map[string][]string) + for ep, actions := range enforcementPointsToActionsMap { + if len(actions) == 0 { + continue + } + enforcementActionsForEPs[ep] = make([]string, 0, len(actions)) + for action := range actions { + enforcementActionsForEPs[ep] = append(enforcementActionsForEPs[ep], action) + } + } + + return enforcementActionsForEPs, nil - return actionsForEPs, nil } // Helper function to access nested fields as an array. diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client.go index 959448968cc..809c675cb0b 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client.go @@ -627,33 +627,21 @@ func (c *Client) RemoveData(ctx context.Context, data interface{}) (*types.Respo // On error, the responses return value will still be populated so that // partial results can be analyzed. func (c *Client) Review(ctx context.Context, obj interface{}, opts ...reviews.ReviewOpt) (*types.Responses, error) { - // Initialize an empty slice for enforcement points var eps []string - // Create a new ReviewCfg instance cfg := &reviews.ReviewCfg{} - // Apply each option to the ReviewCfg instance for _, opt := range opts { opt(cfg) } - - // Check if a source enforcement point is specified in the configuration - if cfg.SourceEP != "" { - // Iterate through the client's enforcement points - for _, ep := range c.enforcementPoints { - if ep == apiconstraints.AllEnforcementPoints || cfg.SourceEP == ep { - eps = append(eps, ep) - break - } - } - // If no enforcement points match the source enforcement point, return nil indicating no review should be run - if eps == nil { - return nil, nil + if cfg.EnforcementPoint == "" { + cfg.EnforcementPoint = apiconstraints.AllEnforcementPoints + } + for _, ep := range c.enforcementPoints { + if cfg.EnforcementPoint == apiconstraints.AllEnforcementPoints || cfg.EnforcementPoint == ep { + eps = append(eps, ep) } } - - // If no specific enforcement points are specified, use the client's enforcement points if eps == nil { - eps = c.enforcementPoints + return nil, fmt.Errorf("%w, supported enforcement points: %v", ErrUnsupportedEnforcementPoints, c.enforcementPoints) } responses := types.NewResponses() diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/constraint_client.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/constraint_client.go index fcf32c7253e..377240085e1 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/constraint_client.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/constraint_client.go @@ -44,7 +44,7 @@ func (c *constraintClient) matches(target string, review interface{}, enforcemen // Iterate over the provided source enforcement points (EPs) for _, ep := range enforcementPoints { var actions []string - if ep == "*" { + if ep == apiconstraints.AllEnforcementPoints { // If the EP is "*", aggregate actions from all EPs for _, acts := range c.enforcementActionsForEP { actions = append(actions, acts...) diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/errors.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/errors.go index b6c918df40c..27412623b8f 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/errors.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/errors.go @@ -5,15 +5,16 @@ import ( ) var ( - ErrCreatingBackend = errors.New("unable to create backend") - ErrNoDriverName = errors.New("driver has no name") - ErrNoReferentialDriver = errors.New("no driver that supports referential constraints added") - ErrDuplicateDriver = errors.New("duplicate drivers of the same name") - ErrCreatingClient = errors.New("unable to create client") - ErrMissingConstraint = errors.New("missing Constraint") - ErrMissingConstraintTemplate = errors.New("missing ConstraintTemplate") - ErrInvalidModule = errors.New("invalid module") - ErrReview = errors.New("target.HandleReview failed") + ErrCreatingBackend = errors.New("unable to create backend") + ErrNoDriverName = errors.New("driver has no name") + ErrNoReferentialDriver = errors.New("no driver that supports referential constraints added") + ErrDuplicateDriver = errors.New("duplicate drivers of the same name") + ErrCreatingClient = errors.New("unable to create client") + ErrMissingConstraint = errors.New("missing Constraint") + ErrMissingConstraintTemplate = errors.New("missing ConstraintTemplate") + ErrInvalidModule = errors.New("invalid module") + ErrReview = errors.New("target.HandleReview failed") + ErrUnsupportedEnforcementPoints = errors.New("enforcement point not supported by client") ) // IsUnrecognizedConstraintError returns true if err is an ErrMissingConstraint. diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews/review_opts.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews/review_opts.go index 32b08d78569..94536baeea8 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews/review_opts.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews/review_opts.go @@ -3,9 +3,9 @@ package reviews import "strings" type ReviewCfg struct { - TracingEnabled bool - StatsEnabled bool - SourceEP string + TracingEnabled bool + StatsEnabled bool + EnforcementPoint string } // ReviewOpt specifies optional arguments for Query driver calls. @@ -28,8 +28,8 @@ func Stats(enabled bool) ReviewOpt { } } -func SourceEP(ep string) ReviewOpt { +func EnforcementPoint(ep string) ReviewOpt { return func(cfg *ReviewCfg) { - cfg.SourceEP = strings.ToLower(ep) + cfg.EnforcementPoint = strings.ToLower(ep) } } diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/template_client.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/template_client.go index 8e1d4ceeb3a..c1ee5b41486 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/template_client.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/template_client.go @@ -97,43 +97,21 @@ func (e *templateClient) Update(templ *templates.ConstraintTemplate, crd *apiext // identical to the stored version. func (e *templateClient) AddConstraint(constraint *unstructured.Unstructured, enforcementPoints []string) (bool, error) { // Initialize a map to hold enforcement actions for each enforcement point (EP) - enforcementActionsForEP := make(map[string][]string) + enforcementActionsForEPs := make(map[string][]string) // Retrieve the enforcement action for the given constraint enforcementAction, err := apiconstraints.GetEnforcementAction(constraint) if err != nil { - return false, err // Return an error if unable to get the enforcement action + return false, err } - // Check if the enforcement action is scoped to specific enforcement points if apiconstraints.IsEnforcementActionScoped(enforcementAction) { - // Retrieve a map of enforcement actions for each EP based on the constraint - enforcementActionsForEPMap, err := apiconstraints.GetEnforcementActionsForEP(constraint, enforcementPoints) + enforcementActionsForEPs, err = apiconstraints.GetEnforcementActionsForEP(constraint, enforcementPoints) if err != nil { - return false, err // Return an error if unable to get the enforcement actions for EPs - } - // Iterate over the map to populate enforcementActionsForEP - for ep, actions := range enforcementActionsForEPMap { - if len(actions) == 0 { - continue // Skip if there are no actions for the current EP - } - // Initialize a slice to hold actions for the current EP, with capacity equal to the number of actions - enforcementActionsForEP[ep] = make([]string, 0, len(actions)) - // Add each action to the slice for the current EP - for action := range actions { - enforcementActionsForEP[ep] = append(enforcementActionsForEP[ep], action) - } + return false, err } } else { - // If the enforcement action is not scoped, or if client does not specify EPs, apply the action to all EPs - if len(enforcementPoints) == 0 { - enforcementPoints = []string{"*"} // Use "*" to represent all EPs - } - // Iterate over the enforcement points to set the enforcement action for each for _, ep := range enforcementPoints { - // Initialize a slice with capacity 1 for the enforcement action - enforcementActionsForEP[ep] = make([]string, 0, 1) - // Add the enforcement action to the slice for the current EP - enforcementActionsForEP[ep] = append(enforcementActionsForEP[ep], enforcementAction) + enforcementActionsForEPs[ep] = append(enforcementActionsForEPs[ep], enforcementAction) } } @@ -156,7 +134,7 @@ func (e *templateClient) AddConstraint(constraint *unstructured.Unstructured, en constraint: cpy, matchers: matchers, enforcementAction: enforcementAction, - enforcementActionsForEP: enforcementActionsForEP, + enforcementActionsForEP: enforcementActionsForEPs, } return true, nil From d7e8d783857cfae0f06e912c3836bbbcffd08cf2 Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Fri, 26 Jul 2024 17:16:05 +0000 Subject: [PATCH 07/23] client must always be initiazed with enforcement point Signed-off-by: Jaydip Gabani --- .../constraint/pkg/apis/constraints/apis.go | 13 +++++-------- .../frameworks/constraint/pkg/client/client_opts.go | 1 - .../frameworks/constraint/pkg/client/new_client.go | 12 ++++++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go index e711860b3b0..85bd0736057 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go @@ -45,6 +45,8 @@ var ( // ErrMissingRequiredField is a specific error that a field is missing from a Constraint. ErrMissingRequiredField = errors.New("missing required field") + + ErrInvalidSpecEnforcementAction = errors.New("scopedEnforcementActions value must be a [{action: string, enforcementPoints: [{name: string}]}]") ) // GetEnforcementAction returns a Constraint's enforcementAction, which indicates @@ -71,13 +73,9 @@ func IsEnforcementActionScoped(action string) bool { // GetEnforcementActionsForEP returns a map of enforcement actions for enforcement points passed in. func GetEnforcementActionsForEP(constraint *unstructured.Unstructured, eps []string) (map[string][]string, error) { - if len(eps) == 0 { - return nil, fmt.Errorf("enforcement points must be provided to get enforcement actions") - } - scopedActions, found, err := getNestedFieldAsArray(constraint.Object, "spec", "scopedEnforcementActions") if err != nil { - return nil, fmt.Errorf("%w: invalid spec.scopedEnforcementActions", ErrInvalidConstraint) + return nil, fmt.Errorf("%w: %w", ErrInvalidSpecEnforcementAction, err) } if !found { return nil, fmt.Errorf("%w: spec.scopedEnforcementActions must be defined", ErrMissingRequiredField) @@ -119,7 +117,6 @@ func GetEnforcementActionsForEP(constraint *unstructured.Unstructured, eps []str } return enforcementActionsForEPs, nil - } // Helper function to access nested fields as an array. @@ -142,12 +139,12 @@ func convertToSliceScopedEnforcementAction(value interface{}) ([]ScopedEnforceme var result []ScopedEnforcementAction arr, ok := value.([]interface{}) if !ok { - return nil, fmt.Errorf("scopedEnforcementActions value must be a []scopedEnforcementAction{action: string, enforcementPoints: []EnforcementPoint{name: string}}") + return nil, ErrInvalidSpecEnforcementAction } for _, v := range arr { m, ok := v.(map[string]interface{}) if !ok { - return nil, fmt.Errorf("scopedEnforcementActions value must be a []scopedEnforcementAction{action: string, enforcementPoints: []EnforcementPoint{name: string}}") + return nil, ErrInvalidSpecEnforcementAction } scopedEA := &ScopedEnforcementAction{} if err := runtime.DefaultUnstructuredConverter.FromUnstructured(m, scopedEA); err != nil { diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client_opts.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client_opts.go index 13da142809a..775b2d9aedd 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client_opts.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client_opts.go @@ -63,7 +63,6 @@ func Driver(d drivers.Driver) Opt { } client.drivers[d.Name()] = d client.driverPriority[d.Name()] = len(client.drivers) - client.enforcementPoints = []string{"*"} return nil } } diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/new_client.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/new_client.go index 0b89f2b875d..ada8010e7e9 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/new_client.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/new_client.go @@ -9,10 +9,9 @@ import ( // NewClient creates a new client. func NewClient(opts ...Opt) (*Client, error) { c := &Client{ - templates: make(map[string]*templateClient), - drivers: make(map[string]drivers.Driver), - driverPriority: make(map[string]int), - enforcementPoints: []string{"*"}, + templates: make(map[string]*templateClient), + drivers: make(map[string]drivers.Driver), + driverPriority: make(map[string]int), } for _, opt := range opts { @@ -26,5 +25,10 @@ func NewClient(opts ...Opt) (*Client, error) { ErrCreatingClient) } + if len(c.enforcementPoints) == 0 { + return nil, fmt.Errorf("%w: must specify at least one enforcement point with client.EnforcementPoints", + ErrCreatingClient) + } + return c, nil } From 2aadeebc0b397301deb0e35356e2c62a768c1a1f Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Fri, 26 Jul 2024 17:32:30 +0000 Subject: [PATCH 08/23] adding test enforcement points in unit tests Signed-off-by: Jaydip Gabani --- pkg/instrumentation/types_test.go | 2 +- pkg/readiness/ready_tracker_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/instrumentation/types_test.go b/pkg/instrumentation/types_test.go index b5b9bea7bd0..d7743cbe828 100644 --- a/pkg/instrumentation/types_test.go +++ b/pkg/instrumentation/types_test.go @@ -15,7 +15,7 @@ func Test_ToStatsEntriesWithDesc(t *testing.T) { driver, err := rego.New() assert.NoError(t, err) - actualClient, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver)) + actualClient, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints("test")) assert.NoError(t, err) testCases := []struct { diff --git a/pkg/readiness/ready_tracker_test.go b/pkg/readiness/ready_tracker_test.go index ac55a6857d4..7ca5e23e977 100644 --- a/pkg/readiness/ready_tracker_test.go +++ b/pkg/readiness/ready_tracker_test.go @@ -106,7 +106,7 @@ func setupDataClient(t *testing.T) *constraintclient.Client { t.Fatalf("setting up Driver: %v", err) } - client, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver)) + client, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints("test")) if err != nil { t.Fatalf("setting up constraint framework client: %v", err) } From 342c3547e2cdb0ba40a5cde760d714fbd2f59bce Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Fri, 26 Jul 2024 18:52:07 +0000 Subject: [PATCH 09/23] fixing unit tests Signed-off-by: Jaydip Gabani --- .../config/config_controller_test.go | 2 +- .../constrainttemplate_controller_test.go | 2 +- ...onstrainttemplatestatus_controller_test.go | 2 +- .../externaldata_controller_test.go | 2 +- pkg/target/target_test.go | 2 +- test/testutils/controller.go | 2 +- .../constraint/pkg/apis/constraints/apis.go | 35 ++++++++++++------- .../pkg/client/constraint_client.go | 15 ++++---- .../k8scel/transform/make_vap_objects.go | 4 +-- .../pkg/client/reviews/review_opts.go | 1 + 10 files changed, 38 insertions(+), 29 deletions(-) diff --git a/pkg/controller/config/config_controller_test.go b/pkg/controller/config/config_controller_test.go index f675be5c947..02cddcc9949 100644 --- a/pkg/controller/config/config_controller_test.go +++ b/pkg/controller/config/config_controller_test.go @@ -414,7 +414,7 @@ func setupController(ctx context.Context, mgr manager.Manager, wm *watch.Manager return nil, fmt.Errorf("unable to set up Driver: %w", err) } - client, err = constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver)) + client, err = constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints("test")) if err != nil { return nil, fmt.Errorf("unable to set up constraint framework data client: %w", err) } diff --git a/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go b/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go index 0a950809eed..d89de9a819b 100644 --- a/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go +++ b/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go @@ -974,7 +974,7 @@ violation[{"msg": "denied!"}] { t.Fatalf("unable to set up Driver: %v", err) } - cfClient, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver)) + cfClient, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints("test")) if err != nil { t.Fatalf("unable to set up constraint framework client: %s", err) } diff --git a/pkg/controller/constrainttemplatestatus/constrainttemplatestatus_controller_test.go b/pkg/controller/constrainttemplatestatus/constrainttemplatestatus_controller_test.go index 435a515ce9f..113d0409d4a 100644 --- a/pkg/controller/constrainttemplatestatus/constrainttemplatestatus_controller_test.go +++ b/pkg/controller/constrainttemplatestatus/constrainttemplatestatus_controller_test.go @@ -114,7 +114,7 @@ violation[{"msg": "denied!"}] { t.Fatalf("unable to set up Driver: %v", err) } - cfClient, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver)) + cfClient, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints("test")) if err != nil { t.Fatalf("unable to set up constraint framework client: %s", err) } diff --git a/pkg/controller/externaldata/externaldata_controller_test.go b/pkg/controller/externaldata/externaldata_controller_test.go index ef986e84a94..340076afac6 100644 --- a/pkg/controller/externaldata/externaldata_controller_test.go +++ b/pkg/controller/externaldata/externaldata_controller_test.go @@ -88,7 +88,7 @@ func TestReconcile(t *testing.T) { t.Fatalf("unable to set up Driver: %v", err) } - cfClient, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver)) + cfClient, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints("test")) if err != nil { t.Fatalf("unable to set up constraint framework client: %s", err) } diff --git a/pkg/target/target_test.go b/pkg/target/target_test.go index 878d3980b09..01f8adb3912 100644 --- a/pkg/target/target_test.go +++ b/pkg/target/target_test.go @@ -31,7 +31,7 @@ func TestFrameworkInjection(t *testing.T) { t.Fatal(err) } - _, err = constraintclient.NewClient(constraintclient.Targets(target), constraintclient.Driver(driver)) + _, err = constraintclient.NewClient(constraintclient.Targets(target), constraintclient.Driver(driver), constraintclient.EnforcementPoints("test")) if err != nil { t.Fatalf("unable to set up OPA client: %s", err) } diff --git a/test/testutils/controller.go b/test/testutils/controller.go index 5b3c55388a1..a9d3cb0bf09 100644 --- a/test/testutils/controller.go +++ b/test/testutils/controller.go @@ -186,7 +186,7 @@ func SetupDataClient(t *testing.T) *constraintclient.Client { t.Fatalf("setting up Driver: %v", err) } - client, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver)) + client, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints("test")) if err != nil { t.Fatalf("setting up constraint framework client: %v", err) } diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go index 85bd0736057..590d31e61e1 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go @@ -14,6 +14,8 @@ type ScopedEnforcementAction struct { EnforcementPoints []EnforcementPoint `json:"enforcementPoints"` } +type EnforcementAction string + type EnforcementPoint struct { Name string `json:"name"` } @@ -22,17 +24,20 @@ const ( // Group is the API Group of Constraints. Group = "constraints.gatekeeper.sh" - // EnforcementActionDeny indicates that if a review fails validation for a + // AllEnforcementPoints is a wildcard to indicate all enforcement points. + AllEnforcementPoints = "*" +) + +const ( + // Deny indicates that if a review fails validation for a // Constraint, that it should be rejected. Errors encountered running // validation are treated as failing validation. // // This is the default EnforcementAction. - EnforcementActionDeny = "deny" - - EnforcementActionScoped = "scoped" - - // AllEnforcementPoints is a wildcard to indicate all enforcement points. - AllEnforcementPoints = "*" + Deny EnforcementAction = "deny" + Warn EnforcementAction = "warn" + Dryrun EnforcementAction = "dryrun" + Scoped EnforcementAction = "scoped" ) var ( @@ -61,14 +66,14 @@ func GetEnforcementAction(constraint *unstructured.Unstructured) (string, error) } if !found { - return EnforcementActionDeny, nil + return string(Deny), nil } return action, nil } func IsEnforcementActionScoped(action string) bool { - return strings.EqualFold(action, EnforcementActionScoped) + return action == string(Scoped) } // GetEnforcementActionsForEP returns a map of enforcement actions for enforcement points passed in. @@ -93,15 +98,21 @@ func GetEnforcementActionsForEP(constraint *unstructured.Unstructured, eps []str for _, scopedEA := range scopedEnforcementActions { for _, enforcementPoint := range scopedEA.EnforcementPoints { epName := strings.ToLower(enforcementPoint.Name) - ea := strings.ToLower(scopedEA.Action) + var action string + switch scopedEA.Action { + case string(Warn), string(Dryrun): + action = scopedEA.Action + default: + action = string(Deny) + } if epName == AllEnforcementPoints { for _, ep := range eps { - enforcementPointsToActionsMap[ep][ea] = true + enforcementPointsToActionsMap[ep][action] = true } break } if _, ok := enforcementPointsToActionsMap[epName]; ok { - enforcementPointsToActionsMap[epName][ea] = true + enforcementPointsToActionsMap[epName][action] = true } } } diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/constraint_client.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/constraint_client.go index 377240085e1..f004badf86e 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/constraint_client.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/constraint_client.go @@ -22,6 +22,7 @@ type constraintClient struct { // enforcementAction is what should be done if the Constraint is violated or // fails to run on a review. + // passed to constraintMatchResult.enforcementAction. enforcementAction string // enforcementActionsForEP stores precompiled enforcement actions for each enforcement point. @@ -38,34 +39,32 @@ func (c *constraintClient) matches(target string, review interface{}, enforcemen return nil } - // Initialize a map to track unique enforcement actions enforcementActions := make(map[string]bool) if apiconstraints.IsEnforcementActionScoped(c.enforcementAction) { - // Iterate over the provided source enforcement points (EPs) for _, ep := range enforcementPoints { var actions []string if ep == apiconstraints.AllEnforcementPoints { - // If the EP is "*", aggregate actions from all EPs for _, acts := range c.enforcementActionsForEP { actions = append(actions, acts...) } } - // Check if there are predefined actions for the current EP if acts, found := c.enforcementActionsForEP[ep]; found { - actions = append(actions, acts...) // Use the predefined actions if found + actions = append(actions, acts...) } - // Mark each action as true in the map to ensure uniqueness for _, act := range actions { enforcementActions[act] = true } } } + // If enforcement action is scoped, constraint does not include enforcement point that needs to be enforced then there is no action to be taken. - // If no enforcement actions are found, return nil if len(enforcementActions) == 0 && apiconstraints.IsEnforcementActionScoped(c.enforcementAction) { return nil } + // If enforcement action is not scoped or constraint needs to be enforced for matching enforcement point, + // Then we need to match the constraint with review. Compute enforcement actions for the enforcement point. + // Pass the enforcement actions and c.enforcementAction to constraintMatchResult. var actions []string for action := range enforcementActions { actions = append(actions, action) @@ -105,10 +104,8 @@ type constraintMatchResult struct { // constraint is a pointer to the Constraint. Not safe for modification. constraint *unstructured.Unstructured // enforcementAction, if specified, is the immediate action to take. - // Only filled in if error is non-nil. enforcementAction string // scopedEnforcementActions are action to take for specific enforcement point. - // Only filled in if error is non-nil. scopedEnforcementActions []string // error is a problem encountered while attempting to run the Constraint's // Matcher. diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/k8scel/transform/make_vap_objects.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/k8scel/transform/make_vap_objects.go index 3180ee6aeb6..3edba376d0d 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/k8scel/transform/make_vap_objects.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/k8scel/transform/make_vap_objects.go @@ -86,9 +86,9 @@ func ConstraintToBinding(constraint *unstructured.Unstructured, actions []string for _, action := range actions { switch action { - case apiconstraints.EnforcementActionDeny: + case string(apiconstraints.Deny): enforcementActions = append(enforcementActions, admissionregistrationv1beta1.Deny) - case "warn": + case string(apiconstraints.Warn): enforcementActions = append(enforcementActions, admissionregistrationv1beta1.Warn) default: return nil, fmt.Errorf("%w: unrecognized enforcement action %s, must be `warn` or `deny`", ErrBadEnforcementAction, action) diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews/review_opts.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews/review_opts.go index 94536baeea8..37ab170db0f 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews/review_opts.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews/review_opts.go @@ -28,6 +28,7 @@ func Stats(enabled bool) ReviewOpt { } } +// EnforcementPoint specifies the enforcement point to use for the query. func EnforcementPoint(ep string) ReviewOpt { return func(cfg *ReviewCfg) { cfg.EnforcementPoint = strings.ToLower(ep) From ee34bd762fbdb0d6e1a76f3854425294fb4cc4b8 Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Sat, 27 Jul 2024 00:05:41 +0000 Subject: [PATCH 10/23] adding test, adding docs Signed-off-by: Jaydip Gabani --- .../constrainttemplate_controller_test.go | 153 +++++++++++++++++- .../constraint/pkg/apis/constraints/apis.go | 12 +- .../pkg/client/constraint_client.go | 13 +- .../constraint/pkg/client/template_client.go | 2 - website/docs/audit.md | 27 ++++ website/docs/enforcement-points.md | 102 ++++++++++++ website/docs/exempt-namespaces.md | 2 + website/docs/validating-admission-policy.md | 45 +++++- website/sidebars.js | 3 +- 9 files changed, 326 insertions(+), 33 deletions(-) create mode 100644 website/docs/enforcement-points.md diff --git a/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go b/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go index d89de9a819b..0bb5ba40e65 100644 --- a/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go +++ b/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go @@ -670,6 +670,144 @@ func TestReconcile(t *testing.T) { } }) + t.Run("Constraint with scoped enforcement actions is marked as enforced", func(t *testing.T) { + suffix := "ScopedMarkedEnforced" + + logger.Info("Running test: Constraint is marked as enforced") + constraintTemplate := makeReconcileConstraintTemplate(suffix) + cstr := newDenyAllCstrWithScopedEA(suffix, util.AuditEnforcementPoint) + + t.Cleanup(testutils.DeleteObjectAndConfirm(ctx, t, c, cstr)) + t.Cleanup(testutils.DeleteObjectAndConfirm(ctx, t, c, expectedCRD(suffix))) + testutils.CreateThenCleanup(ctx, t, c, constraintTemplate) + + err = retry.OnError(testutils.ConstantRetry, func(error) bool { + return true + }, func() error { + return c.Create(ctx, cstr) + }) + if err != nil { + t.Fatal(err) + } + + err = constraintEnforced(ctx, c, suffix) + if err != nil { + t.Fatal(err) + } + + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testns", + }, + } + req := admissionv1.AdmissionRequest{ + Kind: metav1.GroupVersionKind{ + Group: "", + Version: "v1", + Kind: "Namespace", + }, + Operation: "Create", + Name: "FooNamespace", + Object: runtime.RawExtension{Object: ns}, + } + resp, err := cfClient.Review(ctx, req, reviews.EnforcementPoint(util.AuditEnforcementPoint)) + if err != nil { + t.Fatal(err) + } + + gotResults := resp.Results() + if len(gotResults) != 1 { + t.Log(resp.TraceDump()) + t.Log(cfClient.Dump(ctx)) + t.Fatalf("want 1 result, got %v", gotResults) + } + }) + + t.Run("Constraint with enforcement point not supported by client is not marked as enforced", func(t *testing.T) { + suffix := "NotMarkedEnforced" + + logger.Info("Running test: Constraint is marked as enforced") + constraintTemplate := makeReconcileConstraintTemplate(suffix) + cstr := newDenyAllCstrWithScopedEA(suffix, util.WebhookEnforcementPoint) + + t.Cleanup(testutils.DeleteObjectAndConfirm(ctx, t, c, cstr)) + t.Cleanup(testutils.DeleteObjectAndConfirm(ctx, t, c, expectedCRD(suffix))) + testutils.CreateThenCleanup(ctx, t, c, constraintTemplate) + + err = retry.OnError(testutils.ConstantRetry, func(error) bool { + return true + }, func() error { + return c.Create(ctx, cstr) + }) + if err != nil { + t.Fatal(err) + } + + err = constraintEnforced(ctx, c, suffix) + if err != nil { + t.Fatal(err) + } + + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testns", + }, + } + req := admissionv1.AdmissionRequest{ + Kind: metav1.GroupVersionKind{ + Group: "", + Version: "v1", + Kind: "Namespace", + }, + Operation: "Create", + Name: "FooNamespace", + Object: runtime.RawExtension{Object: ns}, + } + resp, err := cfClient.Review(ctx, req, reviews.EnforcementPoint(util.AuditEnforcementPoint)) + if err != nil { + t.Fatal(err) + } + + gotResults := resp.Results() + if len(gotResults) >= 1 { + t.Log(resp.TraceDump()) + t.Log(cfClient.Dump(ctx)) + t.Fatalf("want 0 result, got %v", gotResults) + } + }) + + t.Run("Revew request initiated from an enforcement point not supported by client should result in error", func(t *testing.T) { + suffix := "NotMarkedEnforced" + + logger.Info("Running test: Constraint is marked as enforced") + constraintTemplate := makeReconcileConstraintTemplate(suffix) + cstr := newDenyAllCstrWithScopedEA(suffix, util.WebhookEnforcementPoint) + + t.Cleanup(testutils.DeleteObjectAndConfirm(ctx, t, c, cstr)) + t.Cleanup(testutils.DeleteObjectAndConfirm(ctx, t, c, expectedCRD(suffix))) + testutils.CreateThenCleanup(ctx, t, c, constraintTemplate) + + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testns", + }, + } + req := admissionv1.AdmissionRequest{ + Kind: metav1.GroupVersionKind{ + Group: "", + Version: "v1", + Kind: "Namespace", + }, + Operation: "Create", + Name: "FooNamespace", + Object: runtime.RawExtension{Object: ns}, + } + _, err := cfClient.Review(ctx, req, reviews.EnforcementPoint(util.WebhookEnforcementPoint)) + if err == nil { + t.Fatal("want error, got nil") + } + }) + t.Run("Deleted constraint CRDs are recreated", func(t *testing.T) { suffix := "CRDRecreated" @@ -1227,18 +1365,19 @@ func newDenyAllCstr(suffix string) *unstructured.Unstructured { return cstr } -func newDenyAllCstrWithScopedEA(suffix string, ep string) *unstructured.Unstructured { +func newDenyAllCstrWithScopedEA(suffix string, ep ...string) *unstructured.Unstructured { + pts := make([]interface{}, 0, len(ep)) + for _, e := range ep { + pts = append(pts, map[string]interface{}{"name": e}) + } cstr := &unstructured.Unstructured{ Object: map[string]interface{}{ "spec": map[string]interface{}{ + "enforcementAction": "scoped", "scopedEnforcementActions": []interface{}{ map[string]interface{}{ - "enforcementPoints": []interface{}{ - map[string]interface{}{ - "name": ep, - }, - }, - "action": "deny", + "enforcementPoints": pts, + "action": "deny", }, }, }, diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go index 590d31e61e1..b94f53656d9 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go @@ -36,7 +36,6 @@ const ( // This is the default EnforcementAction. Deny EnforcementAction = "deny" Warn EnforcementAction = "warn" - Dryrun EnforcementAction = "dryrun" Scoped EnforcementAction = "scoped" ) @@ -98,21 +97,14 @@ func GetEnforcementActionsForEP(constraint *unstructured.Unstructured, eps []str for _, scopedEA := range scopedEnforcementActions { for _, enforcementPoint := range scopedEA.EnforcementPoints { epName := strings.ToLower(enforcementPoint.Name) - var action string - switch scopedEA.Action { - case string(Warn), string(Dryrun): - action = scopedEA.Action - default: - action = string(Deny) - } if epName == AllEnforcementPoints { for _, ep := range eps { - enforcementPointsToActionsMap[ep][action] = true + enforcementPointsToActionsMap[ep][scopedEA.Action] = true } break } if _, ok := enforcementPointsToActionsMap[epName]; ok { - enforcementPointsToActionsMap[epName][action] = true + enforcementPointsToActionsMap[epName][scopedEA.Action] = true } } } diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/constraint_client.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/constraint_client.go index f004badf86e..0f801d03167 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/constraint_client.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/constraint_client.go @@ -42,17 +42,10 @@ func (c *constraintClient) matches(target string, review interface{}, enforcemen enforcementActions := make(map[string]bool) if apiconstraints.IsEnforcementActionScoped(c.enforcementAction) { for _, ep := range enforcementPoints { - var actions []string - if ep == apiconstraints.AllEnforcementPoints { - for _, acts := range c.enforcementActionsForEP { - actions = append(actions, acts...) - } - } if acts, found := c.enforcementActionsForEP[ep]; found { - actions = append(actions, acts...) - } - for _, act := range actions { - enforcementActions[act] = true + for _, act := range acts { + enforcementActions[act] = true + } } } } diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/template_client.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/template_client.go index c1ee5b41486..e79b9f5b0e1 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/template_client.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/template_client.go @@ -96,9 +96,7 @@ func (e *templateClient) Update(templ *templates.ConstraintTemplate, crd *apiext // Returns false and no error if the Constraint was not updated due to being // identical to the stored version. func (e *templateClient) AddConstraint(constraint *unstructured.Unstructured, enforcementPoints []string) (bool, error) { - // Initialize a map to hold enforcement actions for each enforcement point (EP) enforcementActionsForEPs := make(map[string][]string) - // Retrieve the enforcement action for the given constraint enforcementAction, err := apiconstraints.GetEnforcementAction(constraint) if err != nil { return false, err diff --git a/website/docs/audit.md b/website/docs/audit.md index 6188c3eba2c..f4ff7ec2f7b 100644 --- a/website/docs/audit.md +++ b/website/docs/audit.md @@ -96,6 +96,7 @@ The audit pod emits JSON-formatted audit logs to stdout. The following is an exa "constraint_name": "container-must-have-limits", "constraint_namespace": "", "constraint_action": "deny", + "constraint_enforcement_actions": [], "constraint_annotations": { "test-annotation-1": "annotation_1" }, @@ -183,6 +184,32 @@ spec: If any of the [constraints](howto.md#constraints) do not specify `kinds`, it will be equivalent to not setting `--audit-match-kind-only` flag (`false` by default), and will fall back to auditing all resources in the cluster. +### Opt-out of Audit in constraints + +By default, all constraints are opted-in audit. To opt-out of the audit process at constraint, you can use `enforcementAction: scoped` and define `scopedEnforcementActions` without including audit enforcement point. + +For example, defining this constraint will opt-out of audit + +```yaml +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sAllowedRepos +metadata: + name: prod-repo-is-openpolicyagent +spec: +... + enforcementAction: scoped + scopedEnforcementActions: + - action: warn + enforcementPoints: + - name: "validation.gatekeeper.sh" + - action: deny + enforcementPoints: + - name: "gator.gatekeeper.sh" +... +``` + +Find out more about different [enforcement points](enforcement-points.md) in Gatekeeper. + ## Audit UserInfo When using `input.review.userInfo`, *NOTE* the request's user's information, such as `username`, `uid`, `groups`, `extra`, cannot be populated by Kubernetes for audit reviews and therefore constraint templates that rely on `userInfo` are not auditable. It is up to the rego author to handle the case where `userInfo` is unset and empty in order to avoid every matching resource being reported as violating resources. diff --git a/website/docs/enforcement-points.md b/website/docs/enforcement-points.md new file mode 100644 index 00000000000..4bfd992f4a1 --- /dev/null +++ b/website/docs/enforcement-points.md @@ -0,0 +1,102 @@ +--- +id: enforcement-points +title: Enforcement points in Gatekeeper +--- + +## Understanding Enforcement Points + +An enforcement point defines the location where enforcement happens. Below are the different enforcement points available in Gatekeeper: + +- `validation.gatekeeper.sh` indicates that enforcement should be carried out by Gatekeeper's validating webhook for a constraint. +- `gator.gatekeeper.sh` indicates that enforcement should be carried out in shift-left via [gator-cli](gator.md) for a constraint. +- `audit.gatekeeper.sh` indicates that on-cluster resources should be audited and violations should be reported for the resources that are in violation of constraint. +- `vap.k8s.io` indicates that enforcement should be carried out by Validating Admission Policy for a constraint. + +### How to use different enforcement points in constraint + +By default, a constraint will be enforced at all enforcement points with common enforcement action defined in `spec.enforcementAction`. However, you can chose to enforce a constraint at specific enforcement points different actions using `spec.scopedEnforcementActions`. Below are the different examples and use cases that utilizes different enforcement actions for different enforcement points. + +> 🗒️ Note: `spec.enforcementAction: scoped` is needed to customize specific enforcement point/enforcement action behavior. If `spec.enforcementAction: scoped` is not provided, `spec.scopedEnforcementActions` is ignored and defined `enforcementAction` will be enforced at all enforcement points. + +###### Deny in shift-left and warn at admission + +You are trying out a new constraint template, and you want deny violating resources in shift-left testing, but do not want to block any resources when admitted to cluster to avoid faulty rejects. You may want to use `deny` action for `gator.gatekeeper.sh` enforcement point and `warn` for `validation.gatekeepet.sh`. The below constraint satisfies this use case. + +```yaml +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sAllowedRepos +metadata: + name: prod-repo-is-openpolicyagent +spec: +... + enforcementAction: scoped + scopedEnforcementActions: + - action: warn + enforcementPoints: + - name: "validation.gatekeeper.sh" + - action: deny + enforcementPoints: + - name: "gator.gatekeeper.sh" +... +``` + +###### Only audit + +You are depending on external-data or referential policies for validating resources. These type of validation may be latency sensitive and may take longer to evaluate. To avoid such situation you may want to only use `audit.gatekeeper.sh` enforcement point to not face any delay at admission time, but still get the information about violating resources from Gatekeeper's audit operation. Here is the constraint for only using `audit.gatekeeper.sh` enforcement point. + +```yaml +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sAllowedRepos +metadata: + name: prod-repo-is-openpolicyagent +spec: +... + enforcementAction: scoped + scopedEnforcementActions: + - action: deny + enforcementPoints: + - name: "audit.gatekeeper.sh" +... +``` + +###### Enforcing through Validating Admission Policy and using Gatekeeper as fall-back validation mechanism + +You want to utilize in-tree Validating Admission Policy for faster turn around time. But you want to make sure that in case Validating Admission Policy fails-open, Gatekeeper blocks faulty resources from being created. Here is how you can achieve the same. + +```yaml +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sAllowedRepos +metadata: + name: prod-repo-is-openpolicyagent +spec: +... + enforcementAction: scoped + scopedEnforcementActions: + - action: deny + enforcementPoints: + - name: "vap.k8s.io" + - name: "validation.gatekeeper.sh" +... +``` + +Please refer to [VAP/VAPB generation behavior](validating-admission-policy.md#policy-updates-to-generate-validating-admission-policy-resources). + +###### Enforcing through Validating Admission Policy and using audit from Gatekeeper + +You want to utilize in-tree Validating Admission Policy for faster turn around time and only want to use audit operation from Gatekeeper to get information about violation resources on-cluster. Here is the constraint that users `vap.k8s.io` and `audit.gatekeeper.sh` enforcement points. + +```yaml +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sAllowedRepos +metadata: + name: prod-repo-is-openpolicyagent +spec: +... + enforcementAction: scoped + scopedEnforcementActions: + - action: deny + enforcementPoints: + - name: "vap.k8s.io" + - name: "audit.gatekeeper.sh" +... +``` diff --git a/website/docs/exempt-namespaces.md b/website/docs/exempt-namespaces.md index e95c1e2b9df..5273c9cbfc9 100644 --- a/website/docs/exempt-namespaces.md +++ b/website/docs/exempt-namespaces.md @@ -31,6 +31,7 @@ spec: ``` Available processes: + - `audit` process exclusion will exclude resources from specified namespace(s) in audit results. - `webhook` process exclusion will exclude resources from specified namespace(s) from the admission webhook. - `sync` process exclusion will exclude resources from specified namespace(s) from being synced into OPA. @@ -52,6 +53,7 @@ If it becomes necessary to exempt a namespace from Gatekeeper webhook entirely ( - key: admission.gatekeeper.sh/ignore operator: DoesNotExist ``` + the default Gatekeeper manifest should already have added this. The default name for the webhook configuration is `gatekeeper-validating-webhook-configuration` and the default name for the webhook that needs the namespace selector is `validation.gatekeeper.sh` diff --git a/website/docs/validating-admission-policy.md b/website/docs/validating-admission-policy.md index 6c7eb088519..a5b55700dd8 100644 --- a/website/docs/validating-admission-policy.md +++ b/website/docs/validating-admission-policy.md @@ -134,7 +134,46 @@ spec: ... ``` -Constraints will follow the behavior defined by `--default-create-vap-binding-for-constraints` flag to generate K8s Validating Admission Policy Binding. By default, `--default-create-vap-binding-for-constraints` is set to `false`. +Constraints will follow the behavior defined in `spec.scopedEnforcementActions`. When `spec.scopedEnforcementAction` is not defined, constraints will follow behavior defined by the flag `--default-create-vap-binding-for-constraints`. By default, `--default-create-vap-binding-for-constraints` is set to `false`. -> [!TIP] -> In the event K8s Validating Admission Controller fails open, Gatekeeper admission webhook can act as a backup. +The overall opt-in/opt-out behavior for constraint to generate Validating Admission Policy Binding (VAPB) is as below: + +Constraint with `enforcementAction: scoped`: + +| `vap.k8s.io` in constraint with `spec.scopedEnforcementActions` | generate VAPB | +|----------|----------| +| Not included | Do not generate VAPB | +| Included | Generate VAPB | + +Constraint with `enforcementAction != scoped`: + +| `--default-create-vap-binding-for-constraints` | generate VAPB | +|----------|----------| +| false | Do not generate VAPB | +| true | Generate VAPB | + +> 🗒️Note: VAPB will not get generated for constraints that belong to templates without CEL engine. + +> 💡TIP: In the event K8s Validating Admission Controller fails open, Gatekeeper admission webhook can act as a backup when included in constraint. + +Validating Admission Policy Binding for the below constraint will always be generated , assuming the constraint belongs to a template with CEL engine. + +```yaml +apiVersion: constraints.gatekeeper.sh/v1beta1 +kind: K8sAllowedRepos +metadata: + name: prod-repo-is-openpolicyagent +spec: +... + enforcementAction: scoped + scopedEnforcementActions: + - action: warn + enforcementPoints: + - name: "validation.gatekeeper.sh" + - action: deny + enforcementPoints: + - name: "vap.k8s.io" +... +``` + +Find out more about different [enforcement points](enforcement-points.md) diff --git a/website/sidebars.js b/website/sidebars.js index 33c8bc384ff..f81ac8de716 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -35,7 +35,8 @@ module.exports = { 'gator', 'workload-resources', 'pubsub', - 'validating-admission-policy' + 'validating-admission-policy', + 'enforcement-points' ], }, { From 2434d04d4402a4fdbe90772480e6cf2264fa7ceb Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Sat, 27 Jul 2024 00:24:11 +0000 Subject: [PATCH 11/23] updating docs Signed-off-by: Jaydip Gabani --- website/docs/enforcement-points.md | 8 ++++---- website/docs/validating-admission-policy.md | 16 +++++++--------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/website/docs/enforcement-points.md b/website/docs/enforcement-points.md index 4bfd992f4a1..261bcaed4ec 100644 --- a/website/docs/enforcement-points.md +++ b/website/docs/enforcement-points.md @@ -7,10 +7,10 @@ title: Enforcement points in Gatekeeper An enforcement point defines the location where enforcement happens. Below are the different enforcement points available in Gatekeeper: -- `validation.gatekeeper.sh` indicates that enforcement should be carried out by Gatekeeper's validating webhook for a constraint. -- `gator.gatekeeper.sh` indicates that enforcement should be carried out in shift-left via [gator-cli](gator.md) for a constraint. -- `audit.gatekeeper.sh` indicates that on-cluster resources should be audited and violations should be reported for the resources that are in violation of constraint. -- `vap.k8s.io` indicates that enforcement should be carried out by Validating Admission Policy for a constraint. +- `validation.gatekeeper.sh` indicates that enforcement should be carried out by Gatekeeper's validating webhook for a constraint. Supports templates with CEL and Rego. +- `gator.gatekeeper.sh` indicates that enforcement should be carried out in shift-left via [gator-cli](gator.md) for a constraint. Supports templates with CEL and Rego. +- `audit.gatekeeper.sh` indicates that on-cluster resources should be audited and violations should be reported for the resources that are in violation of constraint. Supports templates with CEL and Rego. +- `vap.k8s.io` indicates that enforcement should be carried out by Validating Admission Policy for a constraint. Supports templates with CEL. ### How to use different enforcement points in constraint diff --git a/website/docs/validating-admission-policy.md b/website/docs/validating-admission-policy.md index a5b55700dd8..b9500beab03 100644 --- a/website/docs/validating-admission-policy.md +++ b/website/docs/validating-admission-policy.md @@ -24,9 +24,9 @@ To reduce policy fragmentation and simplify the user experience by standardizing The [Constraint Framework](https://github.com/open-policy-agent/frameworks/tree/master/constraint) is the library that underlies Gatekeeper. It provides the execution flow Gatekeeper uses to render a decision to the API server. It also provides abstractions that allow us to define constraint templates and constraints: Engine, Enforcement Points, and Targets. -Together with Gatekeeper and [gator CLI](gator.md), you can get admission, audit, and shift left validations for policies written in both CEL and Rego policy languages, even for clusters that do not support Validating Admission Policy feature yet. For simple policies, you may want admission requests to be handled by the K8s built-in Validating Admission Controller (only supports CEL) instead of the Gatekeeper admission webhook. +Together with Gatekeeper and [gator CLI](gator.md), you can get admission, audit, and shift left validations for policies written in both CEL and Rego policy languages, even for clusters that do not support Validating Admission Policy feature yet. For simple policies, you may want admission requests to be handled by the K8s built-in Validating Admission Controller (only supports CEL) instead of the Gatekeeper admission webhook. -To summary, these are potential options when running Gatekeeper: +To summarize, these are potential options when running Gatekeeper: | Policy Language(s) | Enforcement Point | | ------------------ | ------------------ | @@ -38,6 +38,8 @@ To summary, these are potential options when running Gatekeeper: | Rego | Gatekeeper Audit (referential policies, external data) | | Rego | Gator CLI (referential policies) | +Find out more about different [enforcement points](enforcement-points.md) + ## Pre-requisites - Requires minimum Gatekeeper v3.16.0 @@ -152,11 +154,11 @@ Constraint with `enforcementAction != scoped`: | false | Do not generate VAPB | | true | Generate VAPB | -> 🗒️Note: VAPB will not get generated for constraints that belong to templates without CEL engine. +> ⚠️Warning : VAP will only get generated for templates with CEL Engine. VAPB will only get generated for constraints that belong to templates with CEL engine. > 💡TIP: In the event K8s Validating Admission Controller fails open, Gatekeeper admission webhook can act as a backup when included in constraint. -Validating Admission Policy Binding for the below constraint will always be generated , assuming the constraint belongs to a template with CEL engine. +Validating Admission Policy Binding for the below constraint will always get generated, assuming the constraint belongs to a template with CEL engine. ```yaml apiVersion: constraints.gatekeeper.sh/v1beta1 @@ -167,13 +169,9 @@ spec: ... enforcementAction: scoped scopedEnforcementActions: - - action: warn - enforcementPoints: - - name: "validation.gatekeeper.sh" - action: deny enforcementPoints: - name: "vap.k8s.io" + - name: "validation.gatekeeper.sh" ... ``` - -Find out more about different [enforcement points](enforcement-points.md) From 417d97b17730aa3b159449be5b4eedd3b150fe29 Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Mon, 29 Jul 2024 23:36:03 +0000 Subject: [PATCH 12/23] adding empty enforcement point error Signed-off-by: Jaydip Gabani --- pkg/util/enforcement_action.go | 4 +++- pkg/util/enforcement_action_test.go | 25 ++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/pkg/util/enforcement_action.go b/pkg/util/enforcement_action.go index 917440dcf92..b56d9dde9ab 100644 --- a/pkg/util/enforcement_action.go +++ b/pkg/util/enforcement_action.go @@ -49,6 +49,8 @@ var ErrEnforcementAction = errors.New("unrecognized enforcementAction") // spec.enforcementAction field as it was not a string. var ErrInvalidSpecEnforcementAction = errors.New("spec.enforcementAction must be a string") +var ErrEmptyEnforcementPoint = errors.New("enforcement point cannot be empty") + var ErrInvalidSpecScopedEnforcementAction = errors.New("spec.scopedEnforcementAction must be in the format of []{action: string, enforcementPoints: []{name: string}}") func ValidateEnforcementAction(input EnforcementAction, item map[string]interface{}) error { @@ -84,7 +86,7 @@ func ValidateScopedEnforcementAction(item map[string]interface{}) error { } for _, enforcementPoint := range scopedEnforcementAction.EnforcementPoints { if enforcementPoint.Name == "" { - return ErrInvalidSpecEnforcementAction + return ErrEmptyEnforcementPoint } } } diff --git a/pkg/util/enforcement_action_test.go b/pkg/util/enforcement_action_test.go index e0958500027..5298bf4d725 100644 --- a/pkg/util/enforcement_action_test.go +++ b/pkg/util/enforcement_action_test.go @@ -42,7 +42,7 @@ func TestValidateEnforcementAction(t *testing.T) { Action: "deny", EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "audit", + Name: AuditEnforcementPoint, }, }, }, @@ -50,7 +50,7 @@ func TestValidateEnforcementAction(t *testing.T) { Action: "test", EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "audit", + Name: AuditEnforcementPoint, }, }, }, @@ -67,13 +67,32 @@ func TestValidateEnforcementAction(t *testing.T) { Action: "deny", EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "audit", + Name: AuditEnforcementPoint, + }, + }, + }, + }, + }, + }, + }, + { + name: "Empty enforcement point", + action: Scoped, + constraint: map[string]interface{}{ + "spec": map[string]interface{}{ + "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ + { + Action: "deny", + EnforcementPoints: []apiconstraints.EnforcementPoint{ + { + Name: "", }, }, }, }, }, }, + wantErr: ErrEmptyEnforcementPoint, }, } From cabbfaff17683d026ed538e512a3a99bb6900357 Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Tue, 30 Jul 2024 00:28:52 +0000 Subject: [PATCH 13/23] updating docs Signed-off-by: Jaydip Gabani --- website/docs/enforcement-points.md | 4 +++- website/docs/validating-admission-policy.md | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/website/docs/enforcement-points.md b/website/docs/enforcement-points.md index 261bcaed4ec..f8b9a48f847 100644 --- a/website/docs/enforcement-points.md +++ b/website/docs/enforcement-points.md @@ -16,7 +16,9 @@ An enforcement point defines the location where enforcement happens. Below are t By default, a constraint will be enforced at all enforcement points with common enforcement action defined in `spec.enforcementAction`. However, you can chose to enforce a constraint at specific enforcement points different actions using `spec.scopedEnforcementActions`. Below are the different examples and use cases that utilizes different enforcement actions for different enforcement points. -> 🗒️ Note: `spec.enforcementAction: scoped` is needed to customize specific enforcement point/enforcement action behavior. If `spec.enforcementAction: scoped` is not provided, `spec.scopedEnforcementActions` is ignored and defined `enforcementAction` will be enforced at all enforcement points. +:::note +`spec.enforcementAction: scoped` is needed to customize specific enforcement point/enforcement action behavior. If `spec.enforcementAction: scoped` is not provided, `spec.scopedEnforcementActions` is ignored and defined `enforcementAction` will be enforced at all enforcement points. +::: ###### Deny in shift-left and warn at admission diff --git a/website/docs/validating-admission-policy.md b/website/docs/validating-admission-policy.md index b9500beab03..5ec1d326f0d 100644 --- a/website/docs/validating-admission-policy.md +++ b/website/docs/validating-admission-policy.md @@ -154,9 +154,13 @@ Constraint with `enforcementAction != scoped`: | false | Do not generate VAPB | | true | Generate VAPB | -> ⚠️Warning : VAP will only get generated for templates with CEL Engine. VAPB will only get generated for constraints that belong to templates with CEL engine. +:::note +VAP will only get generated for templates with CEL Engine. VAPB will only get generated for constraints that belong to templates with CEL engine. +::: -> 💡TIP: In the event K8s Validating Admission Controller fails open, Gatekeeper admission webhook can act as a backup when included in constraint. +:::tip +In the event K8s Validating Admission Controller fails open, Gatekeeper admission webhook can act as a backup when included in constraint. +::: Validating Admission Policy Binding for the below constraint will always get generated, assuming the constraint belongs to a template with CEL engine. From 5875a7e8f73c92bec9a0c4c64a8a876fe0d63a01 Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Tue, 30 Jul 2024 01:39:44 +0000 Subject: [PATCH 14/23] refactoring tests, adding enforcement point validation Signed-off-by: Jaydip Gabani --- pkg/audit/manager_test.go | 4 +- .../config/config_controller_test.go | 2 +- .../constraint/constraint_controller.go | 9 ++- .../constraint/constraint_controller_test.go | 20 +++--- .../constrainttemplate_controller_test.go | 2 +- ...onstrainttemplatestatus_controller_test.go | 3 +- .../externaldata_controller_test.go | 2 +- pkg/fakes/fixtures.go | 12 +--- pkg/instrumentation/types_test.go | 3 +- pkg/readiness/ready_tracker_test.go | 2 +- pkg/target/target_test.go | 3 +- pkg/util/enforcement_action.go | 22 ++++-- pkg/util/enforcement_action_test.go | 68 +++++++++---------- pkg/webhook/common.go | 2 +- test/testutils/controller.go | 3 +- 15 files changed, 87 insertions(+), 70 deletions(-) diff --git a/pkg/audit/manager_test.go b/pkg/audit/manager_test.go index b934b162bd2..7f108722260 100644 --- a/pkg/audit/manager_test.go +++ b/pkg/audit/manager_test.go @@ -123,12 +123,12 @@ func Test_auditFromCache(t *testing.T) { { name: "audit excluded from constraint", processExcluder: processExcluderFor([]string{}), - constraint: fakes.WebhookDenyAllConstraint(), + constraint: fakes.ScopedConstraintFor(util.WebhookEnforcementPoint), }, { name: "audit included in constraints", processExcluder: processExcluderFor([]string{}), - constraint: fakes.AuditDenyAllConstraint(), + constraint: fakes.ScopedConstraintFor(util.AuditEnforcementPoint), wantViolation: true, }, } diff --git a/pkg/controller/config/config_controller_test.go b/pkg/controller/config/config_controller_test.go index 02cddcc9949..6de6361ea00 100644 --- a/pkg/controller/config/config_controller_test.go +++ b/pkg/controller/config/config_controller_test.go @@ -414,7 +414,7 @@ func setupController(ctx context.Context, mgr manager.Manager, wm *watch.Manager return nil, fmt.Errorf("unable to set up Driver: %w", err) } - client, err = constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints("test")) + client, err = constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints("audit.gatekeeper.sh")) if err != nil { return nil, fmt.Errorf("unable to set up constraint framework data client: %w", err) } diff --git a/pkg/controller/constraint/constraint_controller.go b/pkg/controller/constraint/constraint_controller.go index b81679ae765..aade96636ef 100755 --- a/pkg/controller/constraint/constraint_controller.go +++ b/pkg/controller/constraint/constraint_controller.go @@ -300,6 +300,13 @@ func (r *ReconcileConstraint) Reconcile(ctx context.Context, request reconcile.R status.Status.Errors = nil if c, err := r.cfClient.GetConstraint(instance); err != nil || !reflect.DeepEqual(instance, c) { + err := util.ValidateEnforcementAction(enforcementAction, instance.Object) + if err != nil { + status.Status.Errors = append(status.Status.Errors, constraintstatusv1beta1.Error{Message: err.Error()}) + if err2 := r.writer.Update(ctx, status); err2 != nil { + log.Error(err2, "could not report error for validation of enforcement action") + } + } generateVAPB, VAPEnforcementActions, err := shouldGenerateVAPB(*DefaultGenerateVAPB, enforcementAction, instance) if err != nil { status.Status.Errors = append(status.Status.Errors, constraintstatusv1beta1.Error{Message: err.Error()}) @@ -373,7 +380,7 @@ func (r *ReconcileConstraint) Reconcile(ctx context.Context, request reconcile.R } // do not generate vapbinding resources // remove if exists - if (!generateVAPB || !HasVAPCel(ct)) && IsVapAPIEnabled() { + if !generateVAPB { currentVapBinding := &admissionregistrationv1beta1.ValidatingAdmissionPolicyBinding{} vapBindingName := fmt.Sprintf("gatekeeper-%s", instance.GetName()) log.Info("check if vapbinding exists", "vapBindingName", vapBindingName) diff --git a/pkg/controller/constraint/constraint_controller_test.go b/pkg/controller/constraint/constraint_controller_test.go index d1404209f12..18d12d2ba16 100644 --- a/pkg/controller/constraint/constraint_controller_test.go +++ b/pkg/controller/constraint/constraint_controller_test.go @@ -134,7 +134,7 @@ func TestShouldGenerateVAPB(t *testing.T) { Action: "deny", EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "webhook", + Name: util.WebhookEnforcementPoint, }, }, }, @@ -142,7 +142,7 @@ func TestShouldGenerateVAPB(t *testing.T) { Action: "warn", EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "webhook", + Name: util.WebhookEnforcementPoint, }, }, }, @@ -166,7 +166,7 @@ func TestShouldGenerateVAPB(t *testing.T) { Action: "deny", EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "audit", + Name: util.AuditEnforcementPoint, }, }, }, @@ -174,7 +174,7 @@ func TestShouldGenerateVAPB(t *testing.T) { Action: "warn", EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "audit", + Name: util.AuditEnforcementPoint, }, }, }, @@ -198,7 +198,7 @@ func TestShouldGenerateVAPB(t *testing.T) { Action: "deny", EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "webhook", + Name: util.WebhookEnforcementPoint, }, { Name: util.VAPEnforcementPoint, @@ -209,7 +209,7 @@ func TestShouldGenerateVAPB(t *testing.T) { Action: "warn", EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "webhook", + Name: util.WebhookEnforcementPoint, }, }, }, @@ -233,7 +233,7 @@ func TestShouldGenerateVAPB(t *testing.T) { Action: "deny", EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "audit", + Name: util.AuditEnforcementPoint, }, { Name: util.VAPEnforcementPoint, @@ -244,7 +244,7 @@ func TestShouldGenerateVAPB(t *testing.T) { Action: "warn", EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "audit", + Name: util.AuditEnforcementPoint, }, { Name: util.VAPEnforcementPoint, @@ -279,7 +279,7 @@ func TestShouldGenerateVAPB(t *testing.T) { Action: "warn", EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "audit", + Name: util.AuditEnforcementPoint, }, }, }, @@ -311,7 +311,7 @@ func TestShouldGenerateVAPB(t *testing.T) { Action: "warn", EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "audit", + Name: util.AuditEnforcementPoint, }, }, }, diff --git a/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go b/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go index 0bb5ba40e65..421b701f493 100644 --- a/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go +++ b/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go @@ -1112,7 +1112,7 @@ violation[{"msg": "denied!"}] { t.Fatalf("unable to set up Driver: %v", err) } - cfClient, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints("test")) + cfClient, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints(util.AuditEnforcementPoint)) if err != nil { t.Fatalf("unable to set up constraint framework client: %s", err) } diff --git a/pkg/controller/constrainttemplatestatus/constrainttemplatestatus_controller_test.go b/pkg/controller/constrainttemplatestatus/constrainttemplatestatus_controller_test.go index 113d0409d4a..4a43271cbd1 100644 --- a/pkg/controller/constrainttemplatestatus/constrainttemplatestatus_controller_test.go +++ b/pkg/controller/constrainttemplatestatus/constrainttemplatestatus_controller_test.go @@ -15,6 +15,7 @@ import ( "github.com/open-policy-agent/gatekeeper/v3/pkg/fakes" "github.com/open-policy-agent/gatekeeper/v3/pkg/readiness" "github.com/open-policy-agent/gatekeeper/v3/pkg/target" + "github.com/open-policy-agent/gatekeeper/v3/pkg/util" "github.com/open-policy-agent/gatekeeper/v3/pkg/watch" testclient "github.com/open-policy-agent/gatekeeper/v3/test/clients" "github.com/open-policy-agent/gatekeeper/v3/test/testutils" @@ -114,7 +115,7 @@ violation[{"msg": "denied!"}] { t.Fatalf("unable to set up Driver: %v", err) } - cfClient, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints("test")) + cfClient, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints(util.AuditEnforcementPoint)) if err != nil { t.Fatalf("unable to set up constraint framework client: %s", err) } diff --git a/pkg/controller/externaldata/externaldata_controller_test.go b/pkg/controller/externaldata/externaldata_controller_test.go index 340076afac6..b695db24dc0 100644 --- a/pkg/controller/externaldata/externaldata_controller_test.go +++ b/pkg/controller/externaldata/externaldata_controller_test.go @@ -88,7 +88,7 @@ func TestReconcile(t *testing.T) { t.Fatalf("unable to set up Driver: %v", err) } - cfClient, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints("test")) + cfClient, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints(util.AuditEnforcementPoint)) if err != nil { t.Fatalf("unable to set up constraint framework client: %s", err) } diff --git a/pkg/fakes/fixtures.go b/pkg/fakes/fixtures.go index ac87e9db05e..11a8b4ae500 100644 --- a/pkg/fakes/fixtures.go +++ b/pkg/fakes/fixtures.go @@ -50,14 +50,6 @@ func DenyAllConstraint() *unstructured.Unstructured { return ConstraintFor("denyall") } -func AuditDenyAllConstraint() *unstructured.Unstructured { - return ScopedConstraintFor(util.AuditEnforcementPoint) -} - -func WebhookDenyAllConstraint() *unstructured.Unstructured { - return ScopedConstraintFor(util.WebhookEnforcementPoint) -} - func ScopedConstraintFor(ep string) *unstructured.Unstructured { u := &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -70,7 +62,7 @@ func ScopedConstraintFor(ep string) *unstructured.Unstructured { "name": ep, }, }, - "action": "deny", + "action": util.Deny, }, map[string]interface{}{ "enforcementPoints": []interface{}{ @@ -78,7 +70,7 @@ func ScopedConstraintFor(ep string) *unstructured.Unstructured { "name": ep, }, }, - "action": "warn", + "action": util.Warn, }, }, }, diff --git a/pkg/instrumentation/types_test.go b/pkg/instrumentation/types_test.go index d7743cbe828..0124f035f88 100644 --- a/pkg/instrumentation/types_test.go +++ b/pkg/instrumentation/types_test.go @@ -7,6 +7,7 @@ import ( "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers/rego" cfinstr "github.com/open-policy-agent/frameworks/constraint/pkg/instrumentation" "github.com/open-policy-agent/gatekeeper/v3/pkg/target" + "github.com/open-policy-agent/gatekeeper/v3/pkg/util" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -15,7 +16,7 @@ func Test_ToStatsEntriesWithDesc(t *testing.T) { driver, err := rego.New() assert.NoError(t, err) - actualClient, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints("test")) + actualClient, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints(util.AuditEnforcementPoint)) assert.NoError(t, err) testCases := []struct { diff --git a/pkg/readiness/ready_tracker_test.go b/pkg/readiness/ready_tracker_test.go index 7ca5e23e977..147e5f184f1 100644 --- a/pkg/readiness/ready_tracker_test.go +++ b/pkg/readiness/ready_tracker_test.go @@ -106,7 +106,7 @@ func setupDataClient(t *testing.T) *constraintclient.Client { t.Fatalf("setting up Driver: %v", err) } - client, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints("test")) + client, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints(util.AuditEnforcementPoint)) if err != nil { t.Fatalf("setting up constraint framework client: %v", err) } diff --git a/pkg/target/target_test.go b/pkg/target/target_test.go index 01f8adb3912..a923439e8aa 100644 --- a/pkg/target/target_test.go +++ b/pkg/target/target_test.go @@ -15,6 +15,7 @@ import ( "github.com/open-policy-agent/gatekeeper/v3/apis/mutations/unversioned" "github.com/open-policy-agent/gatekeeper/v3/pkg/mutation/match" "github.com/open-policy-agent/gatekeeper/v3/pkg/mutation/types" + "github.com/open-policy-agent/gatekeeper/v3/pkg/util" "github.com/open-policy-agent/gatekeeper/v3/pkg/wildcard" admissionv1 "k8s.io/api/admission/v1" corev1 "k8s.io/api/core/v1" @@ -31,7 +32,7 @@ func TestFrameworkInjection(t *testing.T) { t.Fatal(err) } - _, err = constraintclient.NewClient(constraintclient.Targets(target), constraintclient.Driver(driver), constraintclient.EnforcementPoints("test")) + _, err = constraintclient.NewClient(constraintclient.Targets(target), constraintclient.Driver(driver), constraintclient.EnforcementPoints(util.AuditEnforcementPoint)) if err != nil { t.Fatalf("unable to set up OPA client: %s", err) } diff --git a/pkg/util/enforcement_action.go b/pkg/util/enforcement_action.go index b56d9dde9ab..7b2206b989e 100644 --- a/pkg/util/enforcement_action.go +++ b/pkg/util/enforcement_action.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "strings" apiconstraints "github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -33,8 +34,13 @@ const ( // VAP enforcement point for ValidatingAdmissionPolicy. VAPEnforcementPoint = "vap.k8s.io" + + // AllEnforcementPoints indicates all enforcement points. + AllEnforcementPoints = "*" ) +var supportedEnforcementPoints = []string{WebhookEnforcementPoint, AuditEnforcementPoint, GatorEnforcementPoint, VAPEnforcementPoint} + var supportedEnforcementActions = []EnforcementAction{Deny, Dryrun, Warn, Scoped} var supportedScopedActions = []EnforcementAction{Deny, Dryrun, Warn} @@ -49,7 +55,7 @@ var ErrEnforcementAction = errors.New("unrecognized enforcementAction") // spec.enforcementAction field as it was not a string. var ErrInvalidSpecEnforcementAction = errors.New("spec.enforcementAction must be a string") -var ErrEmptyEnforcementPoint = errors.New("enforcement point cannot be empty") +var ErrUnrecognizedEnforcementPoint = errors.New("unrecognized enforcement points") var ErrInvalidSpecScopedEnforcementAction = errors.New("spec.scopedEnforcementAction must be in the format of []{action: string, enforcementPoints: []{name: string}}") @@ -71,6 +77,8 @@ func ValidateScopedEnforcementAction(item map[string]interface{}) error { return fmt.Errorf("error fetching scopedEnforcementActions: %w", err) } + var unrecognizedEnforcementPoints []string + // validating scopedEnforcementActions for _, scopedEnforcementAction := range *obj { if scopedEnforcementAction.Action == "" { @@ -85,11 +93,17 @@ func ValidateScopedEnforcementAction(item map[string]interface{}) error { return ErrInvalidSpecEnforcementAction } for _, enforcementPoint := range scopedEnforcementAction.EnforcementPoints { - if enforcementPoint.Name == "" { - return ErrEmptyEnforcementPoint + switch strings.ToLower(enforcementPoint.Name) { + case WebhookEnforcementPoint, AuditEnforcementPoint, GatorEnforcementPoint, VAPEnforcementPoint, AllEnforcementPoints: + continue + default: + unrecognizedEnforcementPoints = append(unrecognizedEnforcementPoints, enforcementPoint.Name) } } } + if len(unrecognizedEnforcementPoints) > 0 { + return fmt.Errorf("%w: constraint will not be enforced for enforcement points %v, supported enforcement points are %v", ErrUnrecognizedEnforcementPoint, unrecognizedEnforcementPoints, supportedEnforcementPoints) + } return nil } @@ -153,7 +167,7 @@ func ScopedActionForEP(enforcementPoint string, u *unstructured.Unstructured) ([ func enforcementPointEnabled(scopedEnforcementAction apiconstraints.ScopedEnforcementAction, enforcementPoint string) bool { for _, ep := range scopedEnforcementAction.EnforcementPoints { - if ep.Name == enforcementPoint || ep.Name == "*" { + if strings.EqualFold(ep.Name, enforcementPoint) || ep.Name == AllEnforcementPoints { return true } } diff --git a/pkg/util/enforcement_action_test.go b/pkg/util/enforcement_action_test.go index 5298bf4d725..4515c51df1e 100644 --- a/pkg/util/enforcement_action_test.go +++ b/pkg/util/enforcement_action_test.go @@ -39,7 +39,7 @@ func TestValidateEnforcementAction(t *testing.T) { "spec": map[string]interface{}{ "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ { - Action: "deny", + Action: string(Deny), EnforcementPoints: []apiconstraints.EnforcementPoint{ { Name: AuditEnforcementPoint, @@ -64,7 +64,7 @@ func TestValidateEnforcementAction(t *testing.T) { "spec": map[string]interface{}{ "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ { - Action: "deny", + Action: string(Deny), EnforcementPoints: []apiconstraints.EnforcementPoint{ { Name: AuditEnforcementPoint, @@ -82,7 +82,7 @@ func TestValidateEnforcementAction(t *testing.T) { "spec": map[string]interface{}{ "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ { - Action: "deny", + Action: string(Deny), EnforcementPoints: []apiconstraints.EnforcementPoint{ { Name: "", @@ -92,7 +92,7 @@ func TestValidateEnforcementAction(t *testing.T) { }, }, }, - wantErr: ErrEmptyEnforcementPoint, + wantErr: ErrUnrecognizedEnforcementPoint, }, } @@ -177,10 +177,10 @@ func TestGetScopedEnforcementAction(t *testing.T) { "spec": map[string]interface{}{ "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ { - Action: "deny", + Action: string(Deny), EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "audit", + Name: AuditEnforcementPoint, }, }, }, @@ -190,10 +190,10 @@ func TestGetScopedEnforcementAction(t *testing.T) { expectedError: nil, expectedObj: &[]apiconstraints.ScopedEnforcementAction{ { - Action: "deny", + Action: string(Deny), EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "audit", + Name: AuditEnforcementPoint, }, }, }, @@ -242,82 +242,82 @@ func TestScopedActionForEP(t *testing.T) { }{ { name: "valid enforcement point", - enforcementPoint: "audit", + enforcementPoint: AuditEnforcementPoint, item: map[string]interface{}{ "spec": map[string]interface{}{ "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ { - Action: "deny", + Action: string(Deny), EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "audit", + Name: AuditEnforcementPoint, }, }, }, { - Action: "warn", + Action: string(Warn), EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "webhook", + Name: WebhookEnforcementPoint, }, }, }, }, }, }, - expectedActions: []string{"deny"}, + expectedActions: []string{string(Deny)}, expectedError: nil, }, { name: "multiple enforcement points", - enforcementPoint: "webhook", + enforcementPoint: WebhookEnforcementPoint, item: map[string]interface{}{ "spec": map[string]interface{}{ "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ { - Action: "deny", + Action: string(Deny), EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "audit", + Name: AuditEnforcementPoint, }, { - Name: "webhook", + Name: WebhookEnforcementPoint, }, }, }, { - Action: "warn", + Action: string(Warn), EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "webhook", + Name: WebhookEnforcementPoint, }, }, }, }, }, }, - expectedActions: []string{"deny", "warn"}, + expectedActions: []string{string(Deny), string(Warn)}, expectedError: nil, }, { name: "no matching enforcement point", - enforcementPoint: "audit", + enforcementPoint: AuditEnforcementPoint, item: map[string]interface{}{ "spec": map[string]interface{}{ "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ { - Action: "deny", + Action: string(Deny), EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "webhook", + Name: WebhookEnforcementPoint, }, }, }, { - Action: "warn", + Action: string(Warn), EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "webhook", + Name: WebhookEnforcementPoint, }, }, }, @@ -329,35 +329,35 @@ func TestScopedActionForEP(t *testing.T) { }, { name: "wildcard enforcement point", - enforcementPoint: "audit", + enforcementPoint: AuditEnforcementPoint, item: map[string]interface{}{ "spec": map[string]interface{}{ "scopedEnforcementActions": []apiconstraints.ScopedEnforcementAction{ { - Action: "deny", + Action: string(Deny), EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "*", + Name: AllEnforcementPoints, }, }, }, { - Action: "warn", + Action: string(Warn), EnforcementPoints: []apiconstraints.EnforcementPoint{ { - Name: "webhook", + Name: WebhookEnforcementPoint, }, }, }, }, }, }, - expectedActions: []string{"deny"}, + expectedActions: []string{string(Deny)}, expectedError: nil, }, { name: "missing scopedEnforcementActions", - enforcementPoint: "audit", + enforcementPoint: AuditEnforcementPoint, item: map[string]interface{}{ "spec": map[string]interface{}{}, }, @@ -366,7 +366,7 @@ func TestScopedActionForEP(t *testing.T) { }, { name: "invalid scopedEnforcementActions", - enforcementPoint: "audit", + enforcementPoint: AuditEnforcementPoint, item: map[string]interface{}{ "spec": map[string]interface{}{ "scopedEnforcementActions": "invalid", diff --git a/pkg/webhook/common.go b/pkg/webhook/common.go index 193c5ccc13d..7b2306ea3b0 100644 --- a/pkg/webhook/common.go +++ b/pkg/webhook/common.go @@ -48,7 +48,7 @@ var ( runtimeScheme = k8sruntime.NewScheme() codecs = serializer.NewCodecFactory(runtimeScheme) deserializer = codecs.UniversalDeserializer() - disableEnforcementActionValidation = flag.Bool("disable-enforcementaction-validation", false, "disable validation of the enforcementAction field of a constraint") + disableEnforcementActionValidation = flag.Bool("disable-enforcementaction-validation", false, "disable validation of the enforcementAction and scopedEnforcementActions field of a constraint") logDenies = flag.Bool("log-denies", false, "log detailed info on each deny") emitAdmissionEvents = flag.Bool("emit-admission-events", false, "(alpha) emit Kubernetes events for each admission violation") admissionEventsInvolvedNamespace = flag.Bool("admission-events-involved-namespace", false, "emit admission events for each violation in the involved objects namespace, the default (false) generates events in the namespace Gatekeeper is installed in. Admission events from cluster-scoped resources will still follow the default behavior") diff --git a/test/testutils/controller.go b/test/testutils/controller.go index a9d3cb0bf09..ae432e085c5 100644 --- a/test/testutils/controller.go +++ b/test/testutils/controller.go @@ -16,6 +16,7 @@ import ( "github.com/open-policy-agent/gatekeeper/v3/apis" "github.com/open-policy-agent/gatekeeper/v3/pkg/fakes" "github.com/open-policy-agent/gatekeeper/v3/pkg/target" + "github.com/open-policy-agent/gatekeeper/v3/pkg/util" admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1" v1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -186,7 +187,7 @@ func SetupDataClient(t *testing.T) *constraintclient.Client { t.Fatalf("setting up Driver: %v", err) } - client, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints("test")) + client, err := constraintclient.NewClient(constraintclient.Targets(&target.K8sValidationTarget{}), constraintclient.Driver(driver), constraintclient.EnforcementPoints(util.AuditEnforcementPoint)) if err != nil { t.Fatalf("setting up constraint framework client: %v", err) } From 629f56ce8db0397587d3eebdd4c5253f754d8700 Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Tue, 30 Jul 2024 01:58:17 +0000 Subject: [PATCH 15/23] renaming tests Signed-off-by: Jaydip Gabani --- .../constrainttemplate_controller_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go b/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go index 421b701f493..21ca82d3a01 100644 --- a/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go +++ b/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go @@ -673,7 +673,7 @@ func TestReconcile(t *testing.T) { t.Run("Constraint with scoped enforcement actions is marked as enforced", func(t *testing.T) { suffix := "ScopedMarkedEnforced" - logger.Info("Running test: Constraint is marked as enforced") + logger.Info("Running test: Scoped Constraint is marked as enforced") constraintTemplate := makeReconcileConstraintTemplate(suffix) cstr := newDenyAllCstrWithScopedEA(suffix, util.AuditEnforcementPoint) @@ -726,7 +726,7 @@ func TestReconcile(t *testing.T) { t.Run("Constraint with enforcement point not supported by client is not marked as enforced", func(t *testing.T) { suffix := "NotMarkedEnforced" - logger.Info("Running test: Constraint is marked as enforced") + logger.Info("Running test: Constraint is not marked as enforced") constraintTemplate := makeReconcileConstraintTemplate(suffix) cstr := newDenyAllCstrWithScopedEA(suffix, util.WebhookEnforcementPoint) @@ -744,8 +744,8 @@ func TestReconcile(t *testing.T) { } err = constraintEnforced(ctx, c, suffix) - if err != nil { - t.Fatal(err) + if err == nil { + t.Fatal("want error constraint not enforced, got", err) } ns := &corev1.Namespace{ @@ -777,9 +777,9 @@ func TestReconcile(t *testing.T) { }) t.Run("Revew request initiated from an enforcement point not supported by client should result in error", func(t *testing.T) { - suffix := "NotMarkedEnforced" + suffix := "ReviewResultsInError" - logger.Info("Running test: Constraint is marked as enforced") + logger.Info("Running test: Review request initiated from an enforcement point not supported by client should result in error") constraintTemplate := makeReconcileConstraintTemplate(suffix) cstr := newDenyAllCstrWithScopedEA(suffix, util.WebhookEnforcementPoint) From b990ebf57a4d1d451fc91d478b874b40bc5954c6 Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Tue, 30 Jul 2024 02:16:15 +0000 Subject: [PATCH 16/23] refactoring tests Signed-off-by: Jaydip Gabani --- .../constrainttemplate_controller_test.go | 10 +++++----- pkg/fakes/fixtures.go | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go b/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go index 21ca82d3a01..e63dcc417f7 100644 --- a/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go +++ b/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go @@ -723,10 +723,10 @@ func TestReconcile(t *testing.T) { } }) - t.Run("Constraint with enforcement point not supported by client is not marked as enforced", func(t *testing.T) { - suffix := "NotMarkedEnforced" + t.Run("Constraint with different ep than client and review should not be part of the review", func(t *testing.T) { + suffix := "ShouldNotBePartOfReview" - logger.Info("Running test: Constraint is not marked as enforced") + logger.Info("Running test: Constraint with different ep than client and review should not be part of the review") constraintTemplate := makeReconcileConstraintTemplate(suffix) cstr := newDenyAllCstrWithScopedEA(suffix, util.WebhookEnforcementPoint) @@ -744,8 +744,8 @@ func TestReconcile(t *testing.T) { } err = constraintEnforced(ctx, c, suffix) - if err == nil { - t.Fatal("want error constraint not enforced, got", err) + if err != nil { + t.Fatal(err) } ns := &corev1.Namespace{ diff --git a/pkg/fakes/fixtures.go b/pkg/fakes/fixtures.go index 11a8b4ae500..175eab28115 100644 --- a/pkg/fakes/fixtures.go +++ b/pkg/fakes/fixtures.go @@ -62,7 +62,7 @@ func ScopedConstraintFor(ep string) *unstructured.Unstructured { "name": ep, }, }, - "action": util.Deny, + "action": string(util.Deny), }, map[string]interface{}{ "enforcementPoints": []interface{}{ @@ -70,7 +70,7 @@ func ScopedConstraintFor(ep string) *unstructured.Unstructured { "name": ep, }, }, - "action": util.Warn, + "action": string(util.Warn), }, }, }, From 51ecc2d3a97e19c78634a642de15439ed3ebb996 Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Tue, 30 Jul 2024 02:21:00 +0000 Subject: [PATCH 17/23] adding scoped enforcement actions in webhook logging and events Signed-off-by: Jaydip Gabani --- pkg/webhook/policy.go | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/pkg/webhook/policy.go b/pkg/webhook/policy.go index 4202a74231e..5919c57a6cf 100644 --- a/pkg/webhook/policy.go +++ b/pkg/webhook/policy.go @@ -276,6 +276,7 @@ func (h *validationHandler) getValidationMessages(res []*rtypes.Result, req *adm logging.ConstraintAPIVersion, r.Constraint.GroupVersionKind().Version, logging.ConstraintKind, r.Constraint.GetKind(), logging.ConstraintAction, r.EnforcementAction, + logging.ConstraintEnforcementActions, action, logging.ResourceGroup, req.AdmissionRequest.Kind.Group, logging.ResourceAPIVersion, req.AdmissionRequest.Kind.Version, logging.ResourceKind, req.AdmissionRequest.Kind.Kind, @@ -287,19 +288,20 @@ func (h *validationHandler) getValidationMessages(res []*rtypes.Result, req *adm } if *emitAdmissionEvents { annotations := map[string]string{ - logging.Process: "admission", - logging.EventType: "violation", - logging.ConstraintName: r.Constraint.GetName(), - logging.ConstraintGroup: r.Constraint.GroupVersionKind().Group, - logging.ConstraintAPIVersion: r.Constraint.GroupVersionKind().Version, - logging.ConstraintKind: r.Constraint.GetKind(), - logging.ConstraintAction: action, - logging.ResourceGroup: req.AdmissionRequest.Kind.Group, - logging.ResourceAPIVersion: req.AdmissionRequest.Kind.Version, - logging.ResourceKind: req.AdmissionRequest.Kind.Kind, - logging.ResourceNamespace: req.AdmissionRequest.Namespace, - logging.ResourceName: resourceName, - logging.RequestUsername: req.AdmissionRequest.UserInfo.Username, + logging.Process: "admission", + logging.EventType: "violation", + logging.ConstraintName: r.Constraint.GetName(), + logging.ConstraintGroup: r.Constraint.GroupVersionKind().Group, + logging.ConstraintAPIVersion: r.Constraint.GroupVersionKind().Version, + logging.ConstraintKind: r.Constraint.GetKind(), + logging.ConstraintAction: r.EnforcementAction, + logging.ConstraintEnforcementActions: action, + logging.ResourceGroup: req.AdmissionRequest.Kind.Group, + logging.ResourceAPIVersion: req.AdmissionRequest.Kind.Version, + logging.ResourceKind: req.AdmissionRequest.Kind.Kind, + logging.ResourceNamespace: req.AdmissionRequest.Namespace, + logging.ResourceName: resourceName, + logging.RequestUsername: req.AdmissionRequest.UserInfo.Username, } var eventMsg, reason string switch action { From 90717cba5c3ec2e6e8a584718f5f60213e0fc2d2 Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Wed, 31 Jul 2024 01:08:32 +0000 Subject: [PATCH 18/23] removing lowercase conversion for enforcement point Signed-off-by: Jaydip Gabani --- pkg/util/enforcement_action.go | 7 ++- .../frameworks/constraint/deploy/crds.yaml | 45 ++++++++++++------- .../constraint/pkg/apis/constraints/apis.go | 3 +- .../constraint/pkg/client/client_opts.go | 4 -- 4 files changed, 34 insertions(+), 25 deletions(-) diff --git a/pkg/util/enforcement_action.go b/pkg/util/enforcement_action.go index 7b2206b989e..4fbdacf96ce 100644 --- a/pkg/util/enforcement_action.go +++ b/pkg/util/enforcement_action.go @@ -4,7 +4,6 @@ import ( "encoding/json" "errors" "fmt" - "strings" apiconstraints "github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -90,10 +89,10 @@ func ValidateScopedEnforcementAction(item map[string]interface{}) error { return fmt.Errorf("%w: %q is not within the supported list %v", ErrEnforcementAction, scopedEnforcementAction.Action, supportedScopedActions) } if len(scopedEnforcementAction.EnforcementPoints) == 0 { - return ErrInvalidSpecEnforcementAction + return fmt.Errorf("%w: no enforcement points are provided", ErrUnrecognizedEnforcementPoint) } for _, enforcementPoint := range scopedEnforcementAction.EnforcementPoints { - switch strings.ToLower(enforcementPoint.Name) { + switch enforcementPoint.Name { case WebhookEnforcementPoint, AuditEnforcementPoint, GatorEnforcementPoint, VAPEnforcementPoint, AllEnforcementPoints: continue default: @@ -167,7 +166,7 @@ func ScopedActionForEP(enforcementPoint string, u *unstructured.Unstructured) ([ func enforcementPointEnabled(scopedEnforcementAction apiconstraints.ScopedEnforcementAction, enforcementPoint string) bool { for _, ep := range scopedEnforcementAction.EnforcementPoints { - if strings.EqualFold(ep.Name, enforcementPoint) || ep.Name == AllEnforcementPoints { + if ep.Name == enforcementPoint || ep.Name == AllEnforcementPoints { return true } } diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/deploy/crds.yaml b/vendor/github.com/open-policy-agent/frameworks/constraint/deploy/crds.yaml index 83551848f54..61e8feef0de 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/deploy/crds.yaml +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/deploy/crds.yaml @@ -17,7 +17,8 @@ spec: - name: v1 schema: openAPIV3Schema: - description: ConstraintTemplate is the Schema for the constrainttemplates API + description: ConstraintTemplate is the Schema for the constrainttemplates + API properties: apiVersion: description: |- @@ -75,7 +76,8 @@ spec: items: properties: engine: - description: 'The engine used to evaluate the code. Example: "Rego". Required.' + description: 'The engine used to evaluate the code. Example: + "Rego". Required.' type: string source: description: The source code for the template. Required. @@ -110,7 +112,8 @@ spec: properties: errors: items: - description: CreateCRDError represents a single error caught during parsing, compiling, etc. + description: CreateCRDError represents a single error caught + during parsing, compiling, etc. properties: code: type: string @@ -124,7 +127,8 @@ spec: type: object type: array id: - description: a unique identifier for the pod that wrote the status + description: a unique identifier for the pod that wrote the + status type: string observedGeneration: format: int64 @@ -143,7 +147,8 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: ConstraintTemplate is the Schema for the constrainttemplates API + description: ConstraintTemplate is the Schema for the constrainttemplates + API properties: apiVersion: description: |- @@ -201,7 +206,8 @@ spec: items: properties: engine: - description: 'The engine used to evaluate the code. Example: "Rego". Required.' + description: 'The engine used to evaluate the code. Example: + "Rego". Required.' type: string source: description: The source code for the template. Required. @@ -236,7 +242,8 @@ spec: properties: errors: items: - description: CreateCRDError represents a single error caught during parsing, compiling, etc. + description: CreateCRDError represents a single error caught + during parsing, compiling, etc. properties: code: type: string @@ -250,7 +257,8 @@ spec: type: object type: array id: - description: a unique identifier for the pod that wrote the status + description: a unique identifier for the pod that wrote the + status type: string observedGeneration: format: int64 @@ -269,7 +277,8 @@ spec: - name: v1beta1 schema: openAPIV3Schema: - description: ConstraintTemplate is the Schema for the constrainttemplates API + description: ConstraintTemplate is the Schema for the constrainttemplates + API properties: apiVersion: description: |- @@ -327,7 +336,8 @@ spec: items: properties: engine: - description: 'The engine used to evaluate the code. Example: "Rego". Required.' + description: 'The engine used to evaluate the code. Example: + "Rego". Required.' type: string source: description: The source code for the template. Required. @@ -362,7 +372,8 @@ spec: properties: errors: items: - description: CreateCRDError represents a single error caught during parsing, compiling, etc. + description: CreateCRDError represents a single error caught + during parsing, compiling, etc. properties: code: type: string @@ -376,7 +387,8 @@ spec: type: object type: array id: - description: a unique identifier for the pod that wrote the status + description: a unique identifier for the pod that wrote the + status type: string observedGeneration: format: int64 @@ -409,7 +421,8 @@ spec: scope: Cluster versions: - deprecated: true - deprecationWarning: externaldata.gatekeeper.sh/v1alpha1 is deprecated. Use externaldata.gatekeeper.sh/v1beta1 instead. + deprecationWarning: externaldata.gatekeeper.sh/v1alpha1 is deprecated. Use externaldata.gatekeeper.sh/v1beta1 + instead. name: v1alpha1 schema: openAPIV3Schema: @@ -444,7 +457,8 @@ spec: description: Timeout is the timeout when querying the provider. type: integer url: - description: URL is the url for the provider. URL is prefixed with https://. + description: URL is the url for the provider. URL is prefixed with + https://. type: string type: object type: object @@ -484,7 +498,8 @@ spec: description: Timeout is the timeout when querying the provider. type: integer url: - description: URL is the url for the provider. URL is prefixed with https://. + description: URL is the url for the provider. URL is prefixed with + https://. type: string type: object type: object diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go index b94f53656d9..e4ddaf67de8 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/apis/constraints/apis.go @@ -3,7 +3,6 @@ package constraints import ( "errors" "fmt" - "strings" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" @@ -96,7 +95,7 @@ func GetEnforcementActionsForEP(constraint *unstructured.Unstructured, eps []str } for _, scopedEA := range scopedEnforcementActions { for _, enforcementPoint := range scopedEA.EnforcementPoints { - epName := strings.ToLower(enforcementPoint.Name) + epName := enforcementPoint.Name if epName == AllEnforcementPoints { for _, ep := range eps { enforcementPointsToActionsMap[ep][scopedEA.Action] = true diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client_opts.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client_opts.go index 775b2d9aedd..13a8ff90259 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client_opts.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/client_opts.go @@ -4,7 +4,6 @@ import ( "fmt" "regexp" "sort" - "strings" "github.com/open-policy-agent/frameworks/constraint/pkg/client/drivers" "github.com/open-policy-agent/frameworks/constraint/pkg/handler" @@ -76,9 +75,6 @@ func IgnoreNoReferentialDriverWarning(ignore bool) Opt { func EnforcementPoints(eps ...string) Opt { return func(client *Client) error { - for i, ep := range eps { - eps[i] = strings.ToLower(ep) - } client.enforcementPoints = eps return nil } From 5172a87c9f2a251b3e13b2b9c651526d8065d786 Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Wed, 31 Jul 2024 02:33:45 +0000 Subject: [PATCH 19/23] updating with latest frameworks changes Signed-off-by: Jaydip Gabani --- .../frameworks/constraint/pkg/client/reviews/review_opts.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews/review_opts.go b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews/review_opts.go index 37ab170db0f..e4940f2bbd5 100644 --- a/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews/review_opts.go +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/pkg/client/reviews/review_opts.go @@ -1,7 +1,5 @@ package reviews -import "strings" - type ReviewCfg struct { TracingEnabled bool StatsEnabled bool @@ -31,6 +29,6 @@ func Stats(enabled bool) ReviewOpt { // EnforcementPoint specifies the enforcement point to use for the query. func EnforcementPoint(ep string) ReviewOpt { return func(cfg *ReviewCfg) { - cfg.EnforcementPoint = strings.ToLower(ep) + cfg.EnforcementPoint = ep } } From 9837f836dfc1885d57ac4b0145355f6a82698439 Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Wed, 31 Jul 2024 03:48:14 +0000 Subject: [PATCH 20/23] refactoring getValidationMessage for logDenies and emitAdmissionEvents Signed-off-by: Jaydip Gabani --- pkg/readiness/ready_tracker.go | 2 +- pkg/webhook/policy.go | 104 +++++++++++++++++++-------------- 2 files changed, 62 insertions(+), 44 deletions(-) diff --git a/pkg/readiness/ready_tracker.go b/pkg/readiness/ready_tracker.go index 36a1ce7958c..dffec0903e7 100644 --- a/pkg/readiness/ready_tracker.go +++ b/pkg/readiness/ready_tracker.go @@ -46,7 +46,7 @@ import ( var log = logf.Log.WithName("readiness-tracker") // TODO: Uncomment the flag and deleted the boolean constant when we support retry limits (currently the value of the flag is moot without a retry limit since failure won't happen due to unlimited retries) -// var crashOnFailureFetchingExpectations = flag.Bool("crash-on-failure-fetching-expectations", false, "Unless set (defaults to false), gatekeeper will ignore errors when gathering expectations. This prevents bootstrapping errors from crashing Gatekeeper at the cost of increasing the risk Gatekeeper will under-enforce policy. Enabling this will help prevent under-enforcement at the risk of crashing during startup. Note that enabling this flag currently does not achieve the aforementioned effect since fetching expectations will retry until success.") +// var crashOnFailureFetchingExpectations = flag.Bool("crash-on-failure-fetching-expectations", false, "Unless set (defaults to false), gatekeeper will ignore errors when gathering expectations. This prevents bootstrapping errors from crashing Gatekeeper at the cost of increasing the risk Gatekeeper will under-enforce policy. Enabling this will help prevent under-enforcement at the risk of crashing during startup. Note that enabling this flag currently does not achieve the aforementioned effect since fetching expectations will retry until success."). const crashOnFailureFetchingExpectations = false const ( diff --git a/pkg/webhook/policy.go b/pkg/webhook/policy.go index 5919c57a6cf..46411c9fe73 100644 --- a/pkg/webhook/policy.go +++ b/pkg/webhook/policy.go @@ -258,51 +258,65 @@ func (h *validationHandler) getValidationMessages(res []*rtypes.Result, req *adm } } for _, r := range res { - actions := r.ScopedEnforcementActions - if len(actions) == 0 { - actions = []string{r.EnforcementAction} - } - for _, action := range actions { - if err := util.ValidateEnforcementAction(util.EnforcementAction(action), r.Constraint.Object); err != nil { + var actions []string + switch r.EnforcementAction { + case string(util.Scoped): + for _, action := range r.ScopedEnforcementActions { + if err := util.ValidateEnforcementAction(util.EnforcementAction(action), r.Constraint.Object); err != nil { + continue + } + actions = append(actions, action) + } + if len(actions) == 0 { continue } - if *logDenies { - h.log.WithValues( - logging.Process, "admission", - logging.Details, r.Metadata["details"], - logging.EventType, "violation", - logging.ConstraintName, r.Constraint.GetName(), - logging.ConstraintGroup, r.Constraint.GroupVersionKind().Group, - logging.ConstraintAPIVersion, r.Constraint.GroupVersionKind().Version, - logging.ConstraintKind, r.Constraint.GetKind(), - logging.ConstraintAction, r.EnforcementAction, - logging.ConstraintEnforcementActions, action, - logging.ResourceGroup, req.AdmissionRequest.Kind.Group, - logging.ResourceAPIVersion, req.AdmissionRequest.Kind.Version, - logging.ResourceKind, req.AdmissionRequest.Kind.Kind, - logging.ResourceNamespace, req.AdmissionRequest.Namespace, - logging.ResourceName, resourceName, - logging.RequestUsername, req.AdmissionRequest.UserInfo.Username, - ).Info( - fmt.Sprintf("denied admission: %s", r.Msg)) + default: + if err := util.ValidateEnforcementAction(util.EnforcementAction(r.EnforcementAction), r.Constraint.Object); err != nil { + continue } - if *emitAdmissionEvents { - annotations := map[string]string{ - logging.Process: "admission", - logging.EventType: "violation", - logging.ConstraintName: r.Constraint.GetName(), - logging.ConstraintGroup: r.Constraint.GroupVersionKind().Group, - logging.ConstraintAPIVersion: r.Constraint.GroupVersionKind().Version, - logging.ConstraintKind: r.Constraint.GetKind(), - logging.ConstraintAction: r.EnforcementAction, - logging.ConstraintEnforcementActions: action, - logging.ResourceGroup: req.AdmissionRequest.Kind.Group, - logging.ResourceAPIVersion: req.AdmissionRequest.Kind.Version, - logging.ResourceKind: req.AdmissionRequest.Kind.Kind, - logging.ResourceNamespace: req.AdmissionRequest.Namespace, - logging.ResourceName: resourceName, - logging.RequestUsername: req.AdmissionRequest.UserInfo.Username, - } + } + if *logDenies { + h.log.WithValues( + logging.Process, "admission", + logging.Details, r.Metadata["details"], + logging.EventType, "violation", + logging.ConstraintName, r.Constraint.GetName(), + logging.ConstraintGroup, r.Constraint.GroupVersionKind().Group, + logging.ConstraintAPIVersion, r.Constraint.GroupVersionKind().Version, + logging.ConstraintKind, r.Constraint.GetKind(), + logging.ConstraintAction, r.EnforcementAction, + logging.ConstraintEnforcementActions, actions, + logging.ResourceGroup, req.AdmissionRequest.Kind.Group, + logging.ResourceAPIVersion, req.AdmissionRequest.Kind.Version, + logging.ResourceKind, req.AdmissionRequest.Kind.Kind, + logging.ResourceNamespace, req.AdmissionRequest.Namespace, + logging.ResourceName, resourceName, + logging.RequestUsername, req.AdmissionRequest.UserInfo.Username, + ).Info( + fmt.Sprintf("denied admission: %s", r.Msg)) + } + if *emitAdmissionEvents { + annotations := map[string]string{ + logging.Process: "admission", + logging.EventType: "violation", + logging.ConstraintName: r.Constraint.GetName(), + logging.ConstraintGroup: r.Constraint.GroupVersionKind().Group, + logging.ConstraintAPIVersion: r.Constraint.GroupVersionKind().Version, + logging.ConstraintKind: r.Constraint.GetKind(), + logging.ConstraintAction: r.EnforcementAction, + logging.ConstraintEnforcementActions: strings.Join(actions, ","), + logging.ResourceGroup: req.AdmissionRequest.Kind.Group, + logging.ResourceAPIVersion: req.AdmissionRequest.Kind.Version, + logging.ResourceKind: req.AdmissionRequest.Kind.Kind, + logging.ResourceNamespace: req.AdmissionRequest.Namespace, + logging.ResourceName: resourceName, + logging.RequestUsername: req.AdmissionRequest.UserInfo.Username, + } + + if len(actions) == 0 { + actions = append(actions, r.EnforcementAction) + } + for _, action := range actions { var eventMsg, reason string switch action { case string(util.Dryrun): @@ -324,7 +338,11 @@ func (h *validationHandler) getValidationMessages(res []*rtypes.Result, req *adm h.eventRecorder.AnnotatedEventf(ref, annotations, corev1.EventTypeWarning, reason, "%s, Resource Namespace: %s, Constraint: %s, Message: %s", eventMsg, req.AdmissionRequest.Namespace, r.Constraint.GetName(), r.Msg) } } - + } + if len(actions) == 0 { + actions = append(actions, r.EnforcementAction) + } + for _, action := range actions { if action == string(util.Deny) { denyMsgs = append(denyMsgs, fmt.Sprintf("[%s] %s", r.Constraint.GetName(), r.Msg)) } From e3411643a5021b89eb57bdbdce185b0bdb4e6ba5 Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Wed, 31 Jul 2024 06:48:13 +0000 Subject: [PATCH 21/23] adding test for unrecognized enforcement point Signed-off-by: Jaydip Gabani --- .../constrainttemplate_controller_test.go | 51 ++++++++++++++++++- pkg/util/enforcement_action.go | 18 +++---- pkg/webhook/policy.go | 2 + 3 files changed, 60 insertions(+), 11 deletions(-) diff --git a/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go b/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go index e63dcc417f7..3506852c59f 100644 --- a/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go +++ b/pkg/controller/constrainttemplate/constrainttemplate_controller_test.go @@ -804,7 +804,55 @@ func TestReconcile(t *testing.T) { } _, err := cfClient.Review(ctx, req, reviews.EnforcementPoint(util.WebhookEnforcementPoint)) if err == nil { - t.Fatal("want error, got nil") + t.Fatal("want error enforcement point is not supported by client, got nil") + } + }) + + t.Run("Constraint With invalid enforcement point should get error in status", func(t *testing.T) { + suffix := "InvalidEnforcementPoint" + + logger.Info("Running test: Constraint With invalid enforcement point should get error in status") + constraintTemplate := makeReconcileConstraintTemplate(suffix) + cstr := newDenyAllCstrWithScopedEA(suffix, "invalid") + t.Cleanup(testutils.DeleteObjectAndConfirm(ctx, t, c, cstr)) + t.Cleanup(testutils.DeleteObjectAndConfirm(ctx, t, c, expectedCRD(suffix))) + testutils.CreateThenCleanup(ctx, t, c, constraintTemplate) + + err = retry.OnError(testutils.ConstantRetry, func(error) bool { + return true + }, func() error { + return c.Create(ctx, cstr) + }) + if err != nil { + t.Fatal(err) + } + + err = retry.OnError(testutils.ConstantRetry, func(error) bool { + return true + }, func() error { + err := c.Get(ctx, types.NamespacedName{Name: "denyallconstraint"}, cstr) + if err != nil { + return err + } + status, err := getCByPodStatus(cstr) + if err != nil { + return err + } + + errorReported := false + for _, e := range status.Errors { + if strings.Contains(e.Message, "unrecognized enforcement points") { + errorReported = true + break + } + } + if !errorReported { + return fmt.Errorf("want error in constraint status, got %+v", status) + } + return nil + }) + if err != nil { + t.Fatal(err) } }) @@ -1347,7 +1395,6 @@ func constraintEnforced(ctx context.Context, c client.Client, suffix string) err } if !status.Enforced { obj, _ := json.MarshalIndent(cstr.Object, "", " ") - // return errors.New("constraint not enforced) return fmt.Errorf("constraint not enforced: \n%s", obj) } return nil diff --git a/pkg/util/enforcement_action.go b/pkg/util/enforcement_action.go index 4fbdacf96ce..34ff1e48a33 100644 --- a/pkg/util/enforcement_action.go +++ b/pkg/util/enforcement_action.go @@ -77,33 +77,33 @@ func ValidateScopedEnforcementAction(item map[string]interface{}) error { } var unrecognizedEnforcementPoints []string - + var unrecognizedEnforcementActions []string + var errs []error // validating scopedEnforcementActions for _, scopedEnforcementAction := range *obj { - if scopedEnforcementAction.Action == "" { - return ErrInvalidSpecEnforcementAction - } switch EnforcementAction(scopedEnforcementAction.Action) { case Dryrun, Deny, Warn: default: - return fmt.Errorf("%w: %q is not within the supported list %v", ErrEnforcementAction, scopedEnforcementAction.Action, supportedScopedActions) + unrecognizedEnforcementActions = append(unrecognizedEnforcementActions, scopedEnforcementAction.Action) } if len(scopedEnforcementAction.EnforcementPoints) == 0 { - return fmt.Errorf("%w: no enforcement points are provided", ErrUnrecognizedEnforcementPoint) + unrecognizedEnforcementPoints = append(unrecognizedEnforcementPoints, "") } for _, enforcementPoint := range scopedEnforcementAction.EnforcementPoints { switch enforcementPoint.Name { case WebhookEnforcementPoint, AuditEnforcementPoint, GatorEnforcementPoint, VAPEnforcementPoint, AllEnforcementPoints: - continue default: unrecognizedEnforcementPoints = append(unrecognizedEnforcementPoints, enforcementPoint.Name) } } } if len(unrecognizedEnforcementPoints) > 0 { - return fmt.Errorf("%w: constraint will not be enforced for enforcement points %v, supported enforcement points are %v", ErrUnrecognizedEnforcementPoint, unrecognizedEnforcementPoints, supportedEnforcementPoints) + errs = append(errs, fmt.Errorf("%w: constraint will not be enforced for enforcement points %v, supported enforcement points are %v", ErrUnrecognizedEnforcementPoint, unrecognizedEnforcementPoints, supportedEnforcementPoints)) + } + if len(unrecognizedEnforcementActions) > 0 { + errs = append(errs, fmt.Errorf("%w: %v is not within the supported list %v", ErrEnforcementAction, unrecognizedEnforcementActions, supportedScopedActions)) } - return nil + return errors.Join(errs...) } func GetScopedEnforcementAction(item map[string]interface{}) (*[]apiconstraints.ScopedEnforcementAction, error) { diff --git a/pkg/webhook/policy.go b/pkg/webhook/policy.go index 46411c9fe73..6ae3f40402c 100644 --- a/pkg/webhook/policy.go +++ b/pkg/webhook/policy.go @@ -263,6 +263,7 @@ func (h *validationHandler) getValidationMessages(res []*rtypes.Result, req *adm case string(util.Scoped): for _, action := range r.ScopedEnforcementActions { if err := util.ValidateEnforcementAction(util.EnforcementAction(action), r.Constraint.Object); err != nil { + h.log.Error(err, "error validating enforcement action", "skipping enforcement action", action, "constraint", r.Constraint.GetName()) continue } actions = append(actions, action) @@ -272,6 +273,7 @@ func (h *validationHandler) getValidationMessages(res []*rtypes.Result, req *adm } default: if err := util.ValidateEnforcementAction(util.EnforcementAction(r.EnforcementAction), r.Constraint.Object); err != nil { + h.log.Error(err, "error validating enforcement action", "skipping enforcement action", r.EnforcementAction, "constraint", r.Constraint.GetName()) continue } } From a920e36aeb5b3613a0a44aa4382c6a5b9cce869a Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Wed, 31 Jul 2024 17:48:33 +0000 Subject: [PATCH 22/23] bringing frameworks changes Signed-off-by: Jaydip Gabani --- go.mod | 4 +- go.sum | 2 + .../frameworks/constraint/LICENSE | 202 ++++++++++++++++++ vendor/modules.txt | 3 +- 4 files changed, 206 insertions(+), 5 deletions(-) create mode 100644 vendor/github.com/open-policy-agent/frameworks/constraint/LICENSE diff --git a/go.mod b/go.mod index b17ebe0d49d..85c3e4bf1bf 100644 --- a/go.mod +++ b/go.mod @@ -2,8 +2,6 @@ module github.com/open-policy-agent/gatekeeper/v3 go 1.21 -replace github.com/open-policy-agent/frameworks/constraint => /mount/d/go/src/github.com/open-policy-agent/frameworks/constraint - require ( cloud.google.com/go/trace v1.10.11 github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.44.0 @@ -17,7 +15,7 @@ require ( github.com/google/uuid v1.6.0 github.com/onsi/gomega v1.33.1 github.com/open-policy-agent/cert-controller v0.10.1 - github.com/open-policy-agent/frameworks/constraint v0.0.0-20240703202613-6687ba5fafc8 + github.com/open-policy-agent/frameworks/constraint v0.0.0-20240731174015-d12824d75aec github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.19.1 github.com/spf13/cobra v1.8.1 diff --git a/go.sum b/go.sum index b11d3f3f5fe..dcb697c86f2 100644 --- a/go.sum +++ b/go.sum @@ -289,6 +289,8 @@ github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= github.com/open-policy-agent/cert-controller v0.10.1 h1:RXSYoyn8FdCenWecRP//UV5nbVfmstNpj4kHQFkvPK4= github.com/open-policy-agent/cert-controller v0.10.1/go.mod h1:4uRbBLY5DsPOog+a9pqk3JLxuuhrWsbUedQW65HcLTI= +github.com/open-policy-agent/frameworks/constraint v0.0.0-20240731174015-d12824d75aec h1:D9RJ4pCSTHyz3zLuBzexTcDrwaZok5TCjjAOn7yr88A= +github.com/open-policy-agent/frameworks/constraint v0.0.0-20240731174015-d12824d75aec/go.mod h1:xKP/8MfdbByUIIs4DLMKQxDF3G83ZqYy0Glot6d5Xv8= github.com/open-policy-agent/opa v0.66.0 h1:DbrvfJQja0FBRcPOB3Z/BOckocN+M4ApNWyNhSRJt0w= github.com/open-policy-agent/opa v0.66.0/go.mod h1:EIgNnJcol7AvQR/IcWLwL13k64gHVbNAVG46b2G+/EY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= diff --git a/vendor/github.com/open-policy-agent/frameworks/constraint/LICENSE b/vendor/github.com/open-policy-agent/frameworks/constraint/LICENSE new file mode 100644 index 00000000000..8f71f43fee3 --- /dev/null +++ b/vendor/github.com/open-policy-agent/frameworks/constraint/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/vendor/modules.txt b/vendor/modules.txt index aae9d650500..9e24cafde3d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -426,7 +426,7 @@ github.com/onsi/gomega/types # github.com/open-policy-agent/cert-controller v0.10.1 ## explicit; go 1.20 github.com/open-policy-agent/cert-controller/pkg/rotator -# github.com/open-policy-agent/frameworks/constraint v0.0.0-20240703202613-6687ba5fafc8 => /mount/d/go/src/github.com/open-policy-agent/frameworks/constraint +# github.com/open-policy-agent/frameworks/constraint v0.0.0-20240731174015-d12824d75aec ## explicit; go 1.21 github.com/open-policy-agent/frameworks/constraint/deploy github.com/open-policy-agent/frameworks/constraint/pkg/apis @@ -1552,4 +1552,3 @@ sigs.k8s.io/structured-merge-diff/v4/value ## explicit; go 1.12 sigs.k8s.io/yaml sigs.k8s.io/yaml/goyaml.v2 -# github.com/open-policy-agent/frameworks/constraint => /mount/d/go/src/github.com/open-policy-agent/frameworks/constraint From afe799952010c6d618d3971f4e13bda1d2dc408d Mon Sep 17 00:00:00 2001 From: Jaydip Gabani Date: Wed, 31 Jul 2024 20:19:23 +0000 Subject: [PATCH 23/23] updating docs Signed-off-by: Jaydip Gabani --- website/docs/enforcement-points.md | 8 +++++--- website/docs/validating-admission-policy.md | 4 ++-- .../version-v3.16.x/validating-admission-policy.md | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/website/docs/enforcement-points.md b/website/docs/enforcement-points.md index f8b9a48f847..0b7f226b83e 100644 --- a/website/docs/enforcement-points.md +++ b/website/docs/enforcement-points.md @@ -14,15 +14,15 @@ An enforcement point defines the location where enforcement happens. Below are t ### How to use different enforcement points in constraint -By default, a constraint will be enforced at all enforcement points with common enforcement action defined in `spec.enforcementAction`. However, you can chose to enforce a constraint at specific enforcement points different actions using `spec.scopedEnforcementActions`. Below are the different examples and use cases that utilizes different enforcement actions for different enforcement points. +By default, a constraint will be enforced at all enforcement points with common enforcement action defined in `spec.enforcementAction`. However, you can choose to enforce a constraint at specific enforcement points with different actions using `enforcementAction: scoped` and `spec.scopedEnforcementActions`. Below are examples and use cases that utilize different enforcement actions for different enforcement points. :::note -`spec.enforcementAction: scoped` is needed to customize specific enforcement point/enforcement action behavior. If `spec.enforcementAction: scoped` is not provided, `spec.scopedEnforcementActions` is ignored and defined `enforcementAction` will be enforced at all enforcement points. +`spec.enforcementAction: scoped` is needed to customize specific enforcement point/enforcement action behavior. If `spec.enforcementAction: scoped` is not provided, `spec.scopedEnforcementActions` is ignored and the provided `enforcementAction` will be applied across all enforcement points. ::: ###### Deny in shift-left and warn at admission -You are trying out a new constraint template, and you want deny violating resources in shift-left testing, but do not want to block any resources when admitted to cluster to avoid faulty rejects. You may want to use `deny` action for `gator.gatekeeper.sh` enforcement point and `warn` for `validation.gatekeepet.sh`. The below constraint satisfies this use case. +You are trying out a new constraint template, and you want to deny violating resources in shift-left testing, but do not want to block any resources admitted to clusters to reduce impact for faulty rejections. You may want to use `deny` action for the `gator.gatekeeper.sh` shift-left enforcement point and `warn` for `the validation.gatekeepet.sh` admission webhook enforcement point. The below constraint satisfies this use case. ```yaml apiVersion: constraints.gatekeeper.sh/v1beta1 @@ -42,6 +42,8 @@ spec: ... ``` +> **Note**: The audit enforcement point is not included unless explicitly added to scopedEnforcementActions.enforcementPoints or if scopedEnforcementActions.enforcementPoints is set to "*". + ###### Only audit You are depending on external-data or referential policies for validating resources. These type of validation may be latency sensitive and may take longer to evaluate. To avoid such situation you may want to only use `audit.gatekeeper.sh` enforcement point to not face any delay at admission time, but still get the information about violating resources from Gatekeeper's audit operation. Here is the constraint for only using `audit.gatekeeper.sh` enforcement point. diff --git a/website/docs/validating-admission-policy.md b/website/docs/validating-admission-policy.md index 5ec1d326f0d..81d1a26b5b3 100644 --- a/website/docs/validating-admission-policy.md +++ b/website/docs/validating-admission-policy.md @@ -26,7 +26,7 @@ The [Constraint Framework](https://github.com/open-policy-agent/frameworks/tree/ Together with Gatekeeper and [gator CLI](gator.md), you can get admission, audit, and shift left validations for policies written in both CEL and Rego policy languages, even for clusters that do not support Validating Admission Policy feature yet. For simple policies, you may want admission requests to be handled by the K8s built-in Validating Admission Controller (only supports CEL) instead of the Gatekeeper admission webhook. -To summarize, these are potential options when running Gatekeeper: +In summary, these are potential options when running Gatekeeper: | Policy Language(s) | Enforcement Point | | ------------------ | ------------------ | @@ -136,7 +136,7 @@ spec: ... ``` -Constraints will follow the behavior defined in `spec.scopedEnforcementActions`. When `spec.scopedEnforcementAction` is not defined, constraints will follow behavior defined by the flag `--default-create-vap-binding-for-constraints`. By default, `--default-create-vap-binding-for-constraints` is set to `false`. +Gatekeeper determines the intended enforcement actions for a given enforcement point by evaluating what is provided in `spec.scopedEnforcementActions` and `spec.enforcementAction: scoped` in the constraint. If these values are not provided in the constraint, then Gatekeeper will follow behavior defined by the flag `--default-create-vap-binding-for-constraints`. By default, `--default-create-vap-binding-for-constraints` is set to `false`. The overall opt-in/opt-out behavior for constraint to generate Validating Admission Policy Binding (VAPB) is as below: diff --git a/website/versioned_docs/version-v3.16.x/validating-admission-policy.md b/website/versioned_docs/version-v3.16.x/validating-admission-policy.md index 4ce0307a955..bb40232f1d5 100644 --- a/website/versioned_docs/version-v3.16.x/validating-admission-policy.md +++ b/website/versioned_docs/version-v3.16.x/validating-admission-policy.md @@ -26,7 +26,7 @@ The [Constraint Framework](https://github.com/open-policy-agent/frameworks/tree/ Together with Gatekeeper and [gator CLI](gator.md), you can get admission, audit, and shift left validations for policies written in both CEL and Rego policy languages, even for clusters that do not support Validating Admission Policy feature yet. For simple policies, you may want admission requests to be handled by the K8s built-in Validating Admission Controller (only supports CEL) instead of the Gatekeeper admission webhook. -To summary, these are potential options when running Gatekeeper: +In summary, these are potential options when running Gatekeeper: | Policy Language(s) | Enforcement Point | | ------------------ | ------------------ |